[
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"extends\": [\n    \"prettier\",\n    \"plugin:prettier/recommended\",\n    \"plugin:tailwindcss/recommended\",\n    \"plugin:react/recommended\",\n    \"plugin:react-hooks/recommended\",\n    \"eslint:recommended\",\n    \"plugin:@typescript-eslint/recommended\",\n    \"@remix-run/eslint-config\",\n    \"@remix-run/eslint-config/node\"\n  ],\n  \"parser\": \"@typescript-eslint/parser\",\n  \"parserOptions\": {\n    \"project\": [\"./tsconfig.json\"]\n  },\n  \"env\": {\n    \"browser\": true,\n    \"node\": true,\n    \"commonjs\": true,\n    \"es2021\": true\n  },\n  \"rules\": {\n    \"arrow-body-style\": 0,\n    \"comma-dangle\": 0,\n    \"consistent-return\": 0,\n    \"func-names\": 0,\n    \"jsx-a11y/accessible-emoji\": 0,\n    \"jsx-a11y/anchor-is-valid\": [\"warn\", { \"aspects\": [\"invalidHref\"] }],\n    \"jsx-a11y/href-no-hash\": \"off\",\n    \"max-len\": 0,\n    \"no-alert\": 0,\n    \"no-console\": \"warn\",\n    \"no-debugger\": 0,\n    \"no-empty-function\": \"off\",\n    \"no-nested-ternary\": \"off\",\n    \"no-param-reassign\": [2, { \"props\": false }],\n    \"no-shadow\": \"off\",\n    \"no-throw-literal\": \"off\",\n    \"no-underscore-dangle\": 0,\n    \"no-unused-expressions\": [2, { \"allowTaggedTemplates\": true }],\n    \"prefer-const\": [\"error\", { \"destructuring\": \"all\" }],\n    \"prettier/prettier\": [\n      \"error\",\n      {\n        \"trailingComma\": \"all\",\n        \"singleQuote\": true,\n        \"semi\": true,\n        \"printWidth\": 100,\n        \"endOfLine\": \"auto\"\n      }\n    ],\n    \"quotes\": [2, \"single\", { \"avoidEscape\": true, \"allowTemplateLiterals\": true }],\n    \"radix\": 0,\n    \"react-hooks/rules-of-hooks\": \"error\",\n    \"react/display-name\": 1,\n    \"react/forbid-prop-types\": 0,\n    \"react/function-component-definition\": 0,\n    \"react/jsx-filename-extension\": [\"warn\", { \"extensions\": [\".tsx\"] }],\n    \"react/jsx-props-no-spreading\": 0,\n    \"react/no-array-index-key\": 0,\n    \"react/no-unescaped-entities\": 0,\n    \"react/prefer-stateless-function\": 0,\n    \"react/prop-types\": 0,\n    \"react/react-in-jsx-scope\": 0,\n    \"react/require-default-props\": 0,\n    \"space-before-function-paren\": 0,\n    // @typescript-eslint\n    \"@typescript-eslint/no-empty-interface\": \"off\",\n    \"@typescript-eslint/no-throw-literal\": \"off\",\n    \"@typescript-eslint/ban-ts-comment\": \"off\",\n    \"@typescript-eslint/consistent-type-imports\": \"error\",\n    \"@typescript-eslint/no-empty-function\": \"off\",\n    \"@typescript-eslint/no-unused-vars\": [\n      \"warn\",\n      {\n        \"vars\": \"all\",\n        \"args\": \"all\",\n        \"argsIgnorePattern\": \"^_\",\n        \"destructuredArrayIgnorePattern\": \"^_\",\n        \"ignoreRestSiblings\": false\n      }\n    ],\n    //import\n    \"import/no-cycle\": \"error\",\n    \"import/prefer-default-export\": \"off\",\n    \"import/extensions\": [\"error\", \"ignorePackages\", { \"ts\": \"never\", \"tsx\": \"never\" }],\n    \"import/no-extraneous-dependencies\": [\"error\", { \"devDependencies\": true }],\n    \"import/no-duplicates\": \"error\",\n    \"sort-imports\": \"off\",\n    \"tailwindcss/no-custom-classname\": \"off\"\n  },\n  \"ignorePatterns\": [\n    \"node_modules\",\n    \".cache\",\n    \"public\",\n    \".vscode\",\n    \"api\",\n    \"*.config.js\",\n    \"*.lock\",\n    \"*.json\",\n    \"*.yaml\",\n    \"server.ts\",\n    \"build\"\n  ],\n  \"overrides\": [\n    {\n      \"files\": [\n        \"./app/entry.client.tsx\",\n        \"./app/entry.server.tsx\",\n        \"./app/entry.worker.ts\",\n        \"./app/services/**/*.ts\",\n        \"./app/utils/**/*.ts\"\n      ],\n      \"rules\": {\n        \"no-console\": \"off\"\n      }\n    }\n  ],\n  \"plugins\": [\"prettier\", \"react\", \"react-hooks\", \"@typescript-eslint\", \"jsx-a11y\", \"tailwindcss\"],\n  \"settings\": {\n    \"tailwindcss\": {\n      \"callees\": [\"tv\", \"classNames\", \"cn\"]\n    },\n    \"import/resolver\": { \"typescript\": {} },\n    \"react\": { \"version\": \"detect\" }\n  }\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\n\n.cache\n.vercel\n.output\n.env\n.vscode\n.node-persist\n\npublic/build\npublic/sw.js\n/api\nbuild\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\n# check lint/format on staged files\necho \"Run pre-commit tasks\"\npnpm lint-staged --quiet || (\n  echo \"Please lint and format your code before commit!\";\n  false;\n)\n"
  },
  {
    "path": ".prettierignore",
    "content": "/server/index.js\n/public/build\n\n.cache\n.env\n.env-example\n.vercel\n.output\n.node-persist\n\n/build/\n/api\n\nnode_modules\n/.husky\n.vscode/\npnpm-lock.yaml\nremix.config.js\ntsconfig.json"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"trailingComma\": \"all\",\n  \"singleQuote\": true,\n  \"semi\": true,\n  \"printWidth\": 100,\n  \"endOfLine\": \"auto\",\n  \"importOrder\": [\n    \"^(react/(.*)$)|^(react$)\",\n    \"<THIRD_PARTY_MODULES>\",\n    \"\",\n    \"^types$\",\n    \"^~/types/(.*)$\",\n    \"^~/routes/(.*)$\",\n    \"^~/services/(.*)$\",\n    \"^~/utils/(.*)$\",\n    \"^~/context/(.*)$\",\n    \"^~/store/(.*)$\",\n    \"^~/constants/(.*)$\",\n    \"^~/components/layouts/(.*)$\",\n    \"^~/components/media/(.*)$\",\n    \"^~/components/elements/(.*)$\",\n    \"^~/components/styles/(.*)$\",\n    \"^~/components/(.*)$\",\n    \"^~/assets/(.*)$\",\n    \"^~/styles/(.*)$\",\n    \"\",\n    \"^[./]\"\n  ],\n  \"importOrderParserPlugins\": [\"typescript\", \"jsx\", \"decorators-legacy\"],\n  \"importOrderTypeScriptVersion\": \"5.0.0\",\n  \"plugins\": [\"@ianvs/prettier-plugin-sort-imports\"]\n}\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "README.md",
    "content": "<a name=\"readme-top\"></a>\n\n<!-- PROJECT LOGO -->\n<br />\n<div align=\"center\">\n  <a href=\"https://github.com/Khanhtran47/remix-movie\">\n    <img src=\"app/assets/images/logo_loading.png\" alt=\"Logo\" width=\"80\" height=\"80\">\n  </a>\n\n<h2 align=\"center\">SORA</h2>\n  <p align=\"center\">\n    A web app for exploring, watching movies, tv shows and anime built with Remix and NextUI.\n    <br />\n  </p>\n</div>\n\n> **Warning**\n> This app is a work in progress.\n> <br/>\n\n<!-- TABLE OF CONTENTS -->\n<details>\n  <summary>Table of Contents</summary>\n  <ol>\n    <li><a href=\"#tech-stack\">Tech Stack</a></li>\n    <li><a href=\"#development\">Development</a></li>\n    <li><a href=\"#license\">License</a></li>\n  </ol>\n</details>\n<br/>\n\n<div align=\"center\">\n  <a href=\"https://github.com/Khanhtran47/remix-movie\">\n    <img src=\"public/images/screenshot.png\" alt=\"screenshot\">\n  </a>\n</div>\n<br/>\n\n<!-- TECH STACK -->\n\n## Tech Stack\n\n- **Remix** with Typescript\n- **NextUI** for pre-made components and themes\n- Styled using **TailwindCSS** and **Stitches**\n- **SwiperJS** for touch slider\n- Internationalization using **i18n**\n- Authentication, database using **Supabase**\n- UI Components built using **Radix UI**\n- Animation using **Framer Motion**\n- State management using **Zustand**\n- Custom media player using **Artplayer**\n- Cache using **LRU Cache**\n- Color manipulation and conversion using **Tinycolor**\n\n<p align=\"right\">(<a href=\"#readme-top\">back to top</a>)</p>\n\n<!-- DEVELOPMENT -->\n\n## Development\n\n<br/>\n\n1. Install dependencies using pnpm\n\n```sh\npnpm install\n```\n\n2. Copy `.env-example` and update the variables\n\n3. Start the development server\n\n```sh\npnpm dev\n```\n\n<p align=\"right\">(<a href=\"#readme-top\">back to top</a>)</p>\n\n<!-- LICENSE -->\n\n## License\n\nDistributed under the MIT License. See [LICENSE.txt](https://github.com/Khanhtran47/Sora/blob/master/LICENSE.txt) for more information.\n\n<p align=\"right\">(<a href=\"#readme-top\">back to top</a>)</p>\n"
  },
  {
    "path": "app/assets/icons/AnilistStatIcon.tsx",
    "content": "const Good = () => (\n  <path\n    fill=\"rgb(93,193,47)\"\n    stroke=\"none\"\n    d=\"M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm0 448c-110.3 0-200-89.7-200-200S137.7 56 248 56s200 89.7 200 200-89.7 200-200 200zm-80-216c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm160 0c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm4 72.6c-20.8 25-51.5 39.4-84 39.4s-63.2-14.3-84-39.4c-8.5-10.2-23.7-11.5-33.8-3.1-10.2 8.5-11.5 23.6-3.1 33.8 30 36 74.1 56.6 120.9 56.6s90.9-20.6 120.9-56.6c8.5-10.2 7.1-25.3-3.1-33.8-10.1-8.4-25.3-7.1-33.8 3.1z\"\n  />\n);\n\nconst Average = () => (\n  <path\n    fill=\"rgb(247,154,99)\"\n    stroke=\"none\"\n    d=\"M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm0 448c-110.3 0-200-89.7-200-200S137.7 56 248 56s200 89.7 200 200-89.7 200-200 200zm-80-216c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm160-64c-17.7 0-32 14.3-32 32s14.3 32 32 32 32-14.3 32-32-14.3-32-32-32zm8 144H160c-13.2 0-24 10.8-24 24s10.8 24 24 24h176c13.2 0 24-10.8 24-24s-10.8-24-24-24z\"\n  />\n);\n\nconst Bad = () => (\n  <path\n    fill=\"rgb(232,93,117)\"\n    stroke=\"none\"\n    d=\"M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm0 448c-110.3 0-200-89.7-200-200S137.7 56 248 56s200 89.7 200 200-89.7 200-200 200zm-80-216c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm160-64c-17.7 0-32 14.3-32 32s14.3 32 32 32 32-14.3 32-32-14.3-32-32-32zm-80 128c-40.2 0-78 17.7-103.8 48.6-8.5 10.2-7.1 25.3 3.1 33.8 10.2 8.4 25.3 7.1 33.8-3.1 16.6-19.9 41-31.4 66.9-31.4s50.3 11.4 66.9 31.4c8.1 9.7 23.1 11.9 33.8 3.1 10.2-8.5 11.5-23.6 3.1-33.8C326 321.7 288.2 304 248 304z\"\n  />\n);\n\ninterface IAnilistStatProps {\n  /**\n   * The stat to display\n   * @default 'good'\n   * @type 'good' | 'average' | 'bad'\n   * @example 'good'\n   * @example 'average'\n   * @example 'bad'\n   */\n  stat?: 'good' | 'average' | 'bad';\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   * @example 24\n   * @example 32\n   * @example 48\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   * @example 24\n   * @example 32\n   * @example 48\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * @example 24\n   * @example 32\n   * @example 48\n   */\n  width?: number;\n}\n\nconst AnilistStat = ({\n  stat = 'good',\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IAnilistStatProps) => {\n  switch (stat) {\n    case 'average':\n      return (\n        <svg\n          className=\"inline\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 512 512\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          {...props}\n        >\n          <Average />\n        </svg>\n      );\n    case 'bad':\n      return (\n        <svg\n          className=\"inline\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 512 512\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          {...props}\n        >\n          <Bad />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"inline\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 512 512\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          {...props}\n        >\n          <Good />\n        </svg>\n      );\n  }\n};\n\nexport default AnilistStat;\n"
  },
  {
    "path": "app/assets/icons/AnimeIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      fill={color}\n      d=\"M18.223 3.086a1.25 1.25 0 0 1 0 1.768L17.08 5.996h1.17A3.75 3.75 0 0 1 22 9.747v7.5a3.75 3.75 0 0 1-3.75 3.75H5.75A3.75 3.75 0 0 1 2 17.247v-7.5a3.75 3.75 0 0 1 3.75-3.75h1.166L5.775 4.855a1.25 1.25 0 1 1 1.767-1.768l2.652 2.652c.079.079.145.165.198.257h3.213c.053-.092.12-.18.199-.258l2.651-2.652a1.25 1.25 0 0 1 1.768 0zm.027 5.42H5.75a1.25 1.25 0 0 0-1.247 1.157l-.003.094v7.5c0 .659.51 1.199 1.157 1.246l.093.004h12.5a1.25 1.25 0 0 0 1.247-1.157l.003-.093v-7.5c0-.69-.56-1.25-1.25-1.25zm-10 2.5c.69 0 1.25.56 1.25 1.25v1.25a1.25 1.25 0 1 1-2.5 0v-1.25c0-.69.56-1.25 1.25-1.25zm7.5 0c.69 0 1.25.56 1.25 1.25v1.25a1.25 1.25 0 1 1-2.5 0v-1.25c0-.69.56-1.25 1.25-1.25z\"\n    />\n  </g>\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <g>\n    <path\n      fill={color}\n      d=\"M7.172 2.757L10.414 6h3.171l3.243-3.242a1 1 0 0 1 1.415 1.415L16.414 6H18.5A3.5 3.5 0 0 1 22 9.5v8a3.5 3.5 0 0 1-3.5 3.5h-13A3.5 3.5 0 0 1 2 17.5v-8A3.5 3.5 0 0 1 5.5 6h2.085L5.757 4.171a1 1 0 0 1 1.415-1.415zM18.5 8h-13a1.5 1.5 0 0 0-1.493 1.356L4 9.5v8a1.5 1.5 0 0 0 1.356 1.493L5.5 19h13a1.5 1.5 0 0 0 1.493-1.356L20 17.5v-8A1.5 1.5 0 0 0 18.5 8zM8 11a1 1 0 0 1 1 1v2a1 1 0 0 1-2 0v-2a1 1 0 0 1 1-1zm8 0a1 1 0 0 1 1 1v2a1 1 0 0 1-2 0v-2a1 1 0 0 1 1-1z\"\n    />\n  </g>\n);\n\ninterface IAnimeProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst Anime = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: IAnimeProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nAnime.displayName = 'AnimeIcon';\n\nexport default Anime;\n"
  },
  {
    "path": "app/assets/icons/ArrowIcon.tsx",
    "content": "const Right = ({ color }: { color: string }) => (\n  <path\n    d=\"M8.91156 20.6695C8.72156 20.6695 8.53156 20.5995 8.38156 20.4495C8.09156 20.1595 8.09156 19.6795 8.38156 19.3895L14.9016 12.8695C15.3816 12.3895 15.3816 11.6095 14.9016 11.1295L8.38156 4.60953C8.09156 4.31953 8.09156 3.83953 8.38156 3.54953C8.67156 3.25953 9.15156 3.25953 9.44156 3.54953L15.9616 10.0695C16.4716 10.5795 16.7616 11.2695 16.7616 11.9995C16.7616 12.7295 16.4816 13.4195 15.9616 13.9295L9.44156 20.4495C9.29156 20.5895 9.10156 20.6695 8.91156 20.6695Z\"\n    fill={color}\n  />\n);\n\nconst Left = ({ color }: { color: string }) => (\n  <path\n    d=\"M15.0013 20.6695C14.8113 20.6695 14.6213 20.5995 14.4713 20.4495L7.95125 13.9295C6.89125 12.8695 6.89125 11.1295 7.95125 10.0695L14.4713 3.54953C14.7613 3.25953 15.2413 3.25953 15.5312 3.54953C15.8212 3.83953 15.8212 4.31953 15.5312 4.60953L9.01125 11.1295C8.53125 11.6095 8.53125 12.3895 9.01125 12.8695L15.5312 19.3895C15.8212 19.6795 15.8212 20.1595 15.5312 20.4495C15.3813 20.5895 15.1912 20.6695 15.0013 20.6695Z\"\n    fill={color}\n  />\n);\n\nconst Up = ({ color }: { color: string }) => (\n  <path\n    d=\"M19.9195 15.7981C19.7295 15.7981 19.5395 15.7281 19.3895 15.5781L12.8695 9.05813C12.3895 8.57813 11.6095 8.57813 11.1295 9.05813L4.60953 15.5781C4.31953 15.8681 3.83953 15.8681 3.54953 15.5781C3.25953 15.2881 3.25953 14.8081 3.54953 14.5181L10.0695 7.99812C11.1295 6.93813 12.8595 6.93813 13.9295 7.99812L20.4495 14.5181C20.7395 14.8081 20.7395 15.2881 20.4495 15.5781C20.2995 15.7181 20.1095 15.7981 19.9195 15.7981Z\"\n    fill={color}\n  />\n);\n\nconst Down = ({ color }: { color: string }) => (\n  <path\n    d=\"M11.9995 16.8006C11.2995 16.8006 10.5995 16.5306 10.0695 16.0006L3.54953 9.48062C3.25953 9.19062 3.25953 8.71062 3.54953 8.42063C3.83953 8.13063 4.31953 8.13063 4.60953 8.42063L11.1295 14.9406C11.6095 15.4206 12.3895 15.4206 12.8695 14.9406L19.3895 8.42063C19.6795 8.13063 20.1595 8.13063 20.4495 8.42063C20.7395 8.71062 20.7395 9.19062 20.4495 9.48062L13.9295 16.0006C13.3995 16.5306 12.6995 16.8006 11.9995 16.8006Z\"\n    fill={color}\n  />\n);\n\ninterface IArrowProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The direction of the icon\n   * @default 'right'\n   * @type 'right' | 'left' | 'up' | 'down'\n   */\n  direction?: 'right' | 'left' | 'up' | 'down';\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst Arrow = ({\n  fill = 'currentColor',\n  direction = 'right',\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IArrowProps) => {\n  switch (direction) {\n    case 'right':\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Right color={fill} />\n        </svg>\n      );\n    case 'left':\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Left color={fill} />\n        </svg>\n      );\n    case 'up':\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Up color={fill} />\n        </svg>\n      );\n    case 'down':\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Down color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Right color={fill} />\n        </svg>\n      );\n  }\n};\n\nArrow.displayName = 'ArrowIcon';\n\nexport default Arrow;\n"
  },
  {
    "path": "app/assets/icons/BrushIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g data-name=\"Layer 2\">\n    <g data-name=\"brush\">\n      <rect width=\"24\" height=\"24\" opacity=\"0\" />\n      <path\n        d=\"M7.12 12.55a4 4 0 0 0-3.07 3.86v3.11a.47.47 0 0 0 .48.48l3.24-.06a3.78 3.78 0 0 0 3.44-2.2 3.65 3.65 0 0 0-4.09-5.19z\"\n        fill={color}\n      />\n      <path\n        d=\"M19.26 4.46a2.14 2.14 0 0 0-2.88.21L10 11.08a.47.47 0 0 0 0 .66L12.25 14a.47.47 0 0 0 .66 0l6.49-6.47a2.06 2.06 0 0 0 .6-1.47 2 2 0 0 0-.74-1.6z\"\n        fill={color}\n      />\n    </g>\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g data-name=\"Layer 2\">\n    <g data-name=\"brush\">\n      <rect width=\"24\" height=\"24\" opacity=\"0\" />\n      <path\n        d=\"M20 6.83a2.76 2.76 0 0 0-.82-2 2.89 2.89 0 0 0-4 0l-6.6 6.6h-.22a4.42 4.42 0 0 0-4.3 4.31L4 19a1 1 0 0 0 .29.73A1.05 1.05 0 0 0 5 20l3.26-.06a4.42 4.42 0 0 0 4.31-4.3v-.23l6.61-6.6A2.74 2.74 0 0 0 20 6.83zM8.25 17.94L6 18v-2.23a2.4 2.4 0 0 1 2.4-2.36 2.15 2.15 0 0 1 2.15 2.19 2.4 2.4 0 0 1-2.3 2.34zm9.52-10.55l-5.87 5.87a4.55 4.55 0 0 0-.52-.64 3.94 3.94 0 0 0-.64-.52l5.87-5.86a.84.84 0 0 1 1.16 0 .81.81 0 0 1 .23.59.79.79 0 0 1-.23.56z\"\n        fill={color}\n        strokeWidth={strokeWidth}\n      />\n    </g>\n  </g>\n);\n\ninterface IBrushProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst Brush = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IBrushProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nBrush.displayName = 'BrushIcon';\n\nexport default Brush;\n"
  },
  {
    "path": "app/assets/icons/CategoryIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(2 2)\">\n    <path\n      d=\"M14.081,20a2.549,2.549,0,0,1-2.541-2.56V14.031a2.543,2.543,0,0,1,2.541-2.561h3.38A2.549,2.549,0,0,1,20,14.031v3.408A2.554,2.554,0,0,1,17.46,20ZM2.54,20A2.555,2.555,0,0,1,0,17.439V14.031A2.549,2.549,0,0,1,2.54,11.47H5.92a2.543,2.543,0,0,1,2.54,2.561v3.408A2.548,2.548,0,0,1,5.92,20ZM14.081,8.53A2.542,2.542,0,0,1,11.54,5.97V2.561A2.549,2.549,0,0,1,14.081,0h3.38A2.554,2.554,0,0,1,20,2.561V5.97a2.548,2.548,0,0,1-2.54,2.56ZM2.54,8.53A2.548,2.548,0,0,1,0,5.97V2.561A2.555,2.555,0,0,1,2.54,0H5.92A2.549,2.549,0,0,1,8.46,2.561V5.97A2.542,2.542,0,0,1,5.92,8.53Z\"\n      transform=\"translate(0 0)\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g transform=\"translate(2 2)\">\n    <path\n      d=\"M2.449,0H5.716A2.459,2.459,0,0,1,8.163,2.47V5.764a2.46,2.46,0,0,1-2.448,2.47H2.449A2.46,2.46,0,0,1,0,5.764V2.47A2.46,2.46,0,0,1,2.449,0Z\"\n      transform=\"translate(11.837 0)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M2.449,0H5.714A2.46,2.46,0,0,1,8.163,2.47V5.764a2.46,2.46,0,0,1-2.449,2.47H2.449A2.46,2.46,0,0,1,0,5.764V2.47A2.46,2.46,0,0,1,2.449,0Z\"\n      transform=\"translate(0 0)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M2.449,0H5.714A2.46,2.46,0,0,1,8.163,2.471V5.764a2.46,2.46,0,0,1-2.449,2.47H2.449A2.46,2.46,0,0,1,0,5.764V2.471A2.46,2.46,0,0,1,2.449,0Z\"\n      transform=\"translate(0 11.766)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M2.449,0H5.716A2.46,2.46,0,0,1,8.163,2.471V5.764a2.459,2.459,0,0,1-2.448,2.47H2.449A2.46,2.46,0,0,1,0,5.764V2.471A2.46,2.46,0,0,1,2.449,0Z\"\n      transform=\"translate(11.837 11.766)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n  </g>\n);\n\ninterface ICategoryProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The class name of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst Category = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: ICategoryProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={2} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nCategory.displayName = 'CategoryIcon';\n\nexport default Category;\n"
  },
  {
    "path": "app/assets/icons/ChevronDownIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(6 7)\">\n    <path\n      d=\"M4.869,9.631c-.058-.057-.306-.27-.51-.469a21.69,21.69,0,0,1-4.024-5.8A4.617,4.617,0,0,1,0,2.188a1.933,1.933,0,0,1,.218-.9A1.874,1.874,0,0,1,1.122.5,9.84,9.84,0,0,1,2.186.242,23.979,23.979,0,0,1,5.992,0,27.724,27.724,0,0,1,9.681.213a8.495,8.495,0,0,1,1.327.341A1.785,1.785,0,0,1,12,2.132v.057a4.879,4.879,0,0,1-.409,1.321A21.69,21.69,0,0,1,7.625,9.177a5.66,5.66,0,0,1-.554.482A1.783,1.783,0,0,1,6.007,10a1.875,1.875,0,0,1-1.138-.369\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g transform=\"translate(5 8.5)\">\n    <path\n      d=\"M14,0,7,7,0,0\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n  </g>\n);\n\ninterface IChevronDownProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst ChevronDown = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: IChevronDownProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nChevronDown.displayName = 'ChevronDownIcon';\n\nexport default ChevronDown;\n"
  },
  {
    "path": "app/assets/icons/ChevronLeftIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(7 6)\">\n    <path\n      d=\"M.369,4.869c.057-.058.27-.306.469-.51A21.69,21.69,0,0,1,6.633.335,4.617,4.617,0,0,1,7.812,0a1.933,1.933,0,0,1,.9.218,1.874,1.874,0,0,1,.795.9,9.84,9.84,0,0,1,.256,1.064A23.979,23.979,0,0,1,10,5.992a27.724,27.724,0,0,1-.213,3.689,8.495,8.495,0,0,1-.341,1.327A1.785,1.785,0,0,1,7.868,12H7.812a4.879,4.879,0,0,1-1.321-.409A21.69,21.69,0,0,1,.823,7.625a5.66,5.66,0,0,1-.482-.554A1.783,1.783,0,0,1,0,6.007,1.875,1.875,0,0,1,.369,4.869\"\n      transform=\"translate(0)\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g transform=\"translate(15.5 5) rotate(90)\">\n    <path\n      d=\"M14,0,7,7,0,0\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n  </g>\n);\n\ninterface IChevronLeftProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst ChevronLeft = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: IChevronLeftProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nChevronLeft.displayName = 'ChevronLeftIcon';\n\nexport default ChevronLeft;\n"
  },
  {
    "path": "app/assets/icons/ChevronRightIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(7 6)\">\n    <path\n      d=\"M9.631,7.131c-.057.058-.27.306-.469.51a21.69,21.69,0,0,1-5.8,4.024A4.617,4.617,0,0,1,2.188,12a1.933,1.933,0,0,1-.9-.218,1.874,1.874,0,0,1-.795-.9A9.84,9.84,0,0,1,.242,9.814,23.979,23.979,0,0,1,0,6.008,27.724,27.724,0,0,1,.213,2.319,8.495,8.495,0,0,1,.554.992,1.785,1.785,0,0,1,2.132,0h.057A4.879,4.879,0,0,1,3.509.409,21.69,21.69,0,0,1,9.177,4.375a5.66,5.66,0,0,1,.482.554A1.783,1.783,0,0,1,10,5.993a1.875,1.875,0,0,1-.369,1.138\"\n      fill={color}\n    />\n  </g>\n);\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g transform=\"translate(8.5 19) rotate(-90)\">\n    <path\n      d=\"M14,0,7,7,0,0\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n  </g>\n);\n\ninterface IChevronRightProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst ChevronRight = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: IChevronRightProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nChevronRight.displayName = 'ChevronRightIcon';\n\nexport default ChevronRight;\n"
  },
  {
    "path": "app/assets/icons/ChevronUpIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(6 7)\">\n    <path\n      d=\"M7.131.369c.058.057.306.27.51.469a21.69,21.69,0,0,1,4.024,5.8A4.617,4.617,0,0,1,12,7.812a1.933,1.933,0,0,1-.218.9,1.874,1.874,0,0,1-.9.795,9.84,9.84,0,0,1-1.064.256A23.979,23.979,0,0,1,6.008,10a27.724,27.724,0,0,1-3.689-.213A8.495,8.495,0,0,1,.992,9.446,1.785,1.785,0,0,1,0,7.868V7.812A4.879,4.879,0,0,1,.409,6.491,21.69,21.69,0,0,1,4.375.823,5.66,5.66,0,0,1,4.929.341,1.783,1.783,0,0,1,5.993,0,1.875,1.875,0,0,1,7.131.369\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g transform=\"translate(19 15.5) rotate(180)\">\n    <path\n      d=\"M14,0,7,7,0,0\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n  </g>\n);\n\ninterface IChevronUpProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst ChevronUp = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: IChevronUpProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nChevronUp.displayName = 'ChevronUpIcon';\n\nexport default ChevronUp;\n"
  },
  {
    "path": "app/assets/icons/CloseIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(2.5 2)\">\n    <path\n      d=\"M12 2C6.49 2 2 6.49 2 12C2 17.51 6.49 22 12 22C17.51 22 22 17.51 22 12C22 6.49 17.51 2 12 2ZM15.36 14.3C15.65 14.59 15.65 15.07 15.36 15.36C15.21 15.51 15.02 15.58 14.83 15.58C14.64 15.58 14.45 15.51 14.3 15.36L12 13.06L9.7 15.36C9.55 15.51 9.36 15.58 9.17 15.58C8.98 15.58 8.79 15.51 8.64 15.36C8.35 15.07 8.35 14.59 8.64 14.3L10.94 12L8.64 9.7C8.35 9.41 8.35 8.93 8.64 8.64C8.93 8.35 9.41 8.35 9.7 8.64L12 10.94L14.3 8.64C14.59 8.35 15.07 8.35 15.36 8.64C15.65 8.93 15.65 9.41 15.36 9.7L13.06 12L15.36 14.3Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <path\n    d=\"M17.6585 4.92888C18.049 4.53836 18.6822 4.53835 19.0727 4.92888C19.4632 5.3194 19.4632 5.95257 19.0727 6.34309L13.4158 12L19.0727 17.6568C19.4632 18.0473 19.4632 18.6805 19.0727 19.071C18.6822 19.4615 18.049 19.4615 17.6585 19.071L12.0016 13.4142L6.34481 19.071C6.3387 19.0771 6.33254 19.0831 6.32632 19.089C5.93455 19.4614 5.31501 19.4554 4.93059 19.071C4.6377 18.7781 4.56447 18.3487 4.71092 17.9876C4.75973 17.8672 4.83296 17.7544 4.93059 17.6568L10.5874 12L4.93059 6.34314C4.54006 5.95262 4.54006 5.31945 4.93059 4.92893C5.32111 4.5384 5.95428 4.5384 6.3448 4.92893L12.0016 10.5857L17.6585 4.92888Z\"\n    fill={color}\n  />\n);\n\ninterface ICloseProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The class name of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst Close = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: ICloseProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nClose.displayName = 'CloseIcon';\n\nexport default Close;\n"
  },
  {
    "path": "app/assets/icons/DiscoverIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M20.9808 3.02084C20.1108 2.15084 18.8808 1.81084 17.6908 2.11084L7.89084 4.56084C6.24084 4.97084 4.97084 6.25084 4.56084 7.89084L2.11084 17.7008C1.81084 18.8908 2.15084 20.1208 3.02084 20.9908C3.68084 21.6408 4.55084 22.0008 5.45084 22.0008C5.73084 22.0008 6.02084 21.9708 6.30084 21.8908L16.1108 19.4408C17.7508 19.0308 19.0308 17.7608 19.4408 16.1108L21.8908 6.30084C22.1908 5.11084 21.8508 3.88084 20.9808 3.02084ZM12.0008 15.8808C9.86084 15.8808 8.12084 14.1408 8.12084 12.0008C8.12084 9.86084 9.86084 8.12084 12.0008 8.12084C14.1408 8.12084 15.8808 9.86084 15.8808 12.0008C15.8808 14.1408 14.1408 15.8808 12.0008 15.8808Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M4.90926 22.8207C3.81926 22.8207 2.88926 22.4707 2.20926 21.7907C1.23926 20.8207 0.939261 19.3407 1.36926 17.6207L3.84926 7.69075C4.27926 5.97075 5.95926 4.30075 7.66926 3.87075L17.5993 1.39075C19.3193 0.960746 20.7993 1.26075 21.7693 2.23075C22.7393 3.20075 23.0393 4.68075 22.6093 6.40075L20.1293 16.3307C19.6993 18.0507 18.0193 19.7207 16.3093 20.1507L6.37926 22.6307C5.86926 22.7507 5.37926 22.8207 4.90926 22.8207ZM17.9793 2.83075L8.04926 5.32075C6.87926 5.61075 5.60926 6.88075 5.30926 8.05075L2.82926 17.9807C2.52926 19.1707 2.68926 20.1407 3.26926 20.7307C3.84926 21.3107 4.82926 21.4707 6.01926 21.1707L15.9493 18.6907C17.1193 18.4007 18.3893 17.1207 18.6793 15.9607L21.1593 6.03075C21.4593 4.84075 21.2993 3.87075 20.7193 3.28075C20.1393 2.69075 19.1693 2.54075 17.9793 2.83075Z\"\n      fill={color}\n    />\n    <path\n      d=\"M12 16.25C9.66 16.25 7.75 14.34 7.75 12C7.75 9.66 9.66 7.75 12 7.75C14.34 7.75 16.25 9.66 16.25 12C16.25 14.34 14.34 16.25 12 16.25ZM12 9.25C10.48 9.25 9.25 10.48 9.25 12C9.25 13.52 10.48 14.75 12 14.75C13.52 14.75 14.75 13.52 14.75 12C14.75 10.48 13.52 9.25 12 9.25Z\"\n      fill={color}\n    />\n  </g>\n);\n\ninterface IDiscoverProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst Discover = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: IDiscoverProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nDiscover.displayName = 'DiscoverIcon';\n\nexport default Discover;\n"
  },
  {
    "path": "app/assets/icons/ExpandIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"scale(-1 1) translate(-23 -1)\">\n    <path\n      d=\"M11 16.15V18.85C11 21.1 10.1 22 7.85 22H5.15C2.9 22 2 21.1 2 18.85V16.15C2 13.9 2.9 13 5.15 13H7.85C10.1 13 11 13.9 11 16.15Z\"\n      fill={color}\n    />\n    <path\n      d=\"M16.48 2H8.52C5.47 2 3 4.47 3 7.52V10.5C3 11.05 3.45 11.5 4 11.5H8.5C10.71 11.5 12.5 13.29 12.5 15.5V20C12.5 20.55 12.95 21 13.5 21H16.48C19.93 21 22 18.94 22 15.48V7.52C22 4.47 19.53 2 16.48 2ZM18.76 9.99C18.76 10.4 18.42 10.74 18.01 10.74C17.6 10.74 17.26 10.4 17.26 9.99V7.79L13.53 11.53C13.38 11.68 13.19 11.75 13 11.75C12.81 11.75 12.62 11.68 12.47 11.53C12.18 11.24 12.18 10.76 12.47 10.47L16.2 6.72H14C13.58 6.72 13.25 6.39 13.25 5.97C13.25 5.56 13.58 5.22 14 5.22H18.01C18.1 5.22 18.19 5.25 18.27 5.28C18.3 5.29 18.32 5.3 18.34 5.31C18.4 5.34 18.45 5.37 18.5 5.42C18.52 5.43 18.54 5.45 18.56 5.47C18.61 5.53 18.65 5.59 18.69 5.66C18.69 5.67 18.7 5.68 18.7 5.69C18.74 5.77 18.75 5.86 18.75 5.95C18.76 5.96 18.76 5.96 18.76 5.97V9.99Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g transform=\"scale(-1 1) translate(-23 -1)\">\n    <path\n      d=\"M2 9.98V9C2 4 4 2 9 2H15C20 2 22 4 22 9V15C22 20 20 22 15 22H14\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M13 11L18.01 5.97998H14\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M18.01 5.97998V9.98998\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M11 16.15V18.85C11 21.1 10.1 22 7.85 22H5.15C2.9 22 2 21.1 2 18.85V16.15C2 13.9 2.9 13 5.15 13H7.85C10.1 13 11 13.9 11 16.15Z\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n  </g>\n);\n\ninterface IExpandProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst Expand = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IExpandProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nExpand.displayName = 'ExpandIcon';\n\nexport default Expand;\n"
  },
  {
    "path": "app/assets/icons/FilterIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(2 3)\">\n    <path\n      d=\"M13.122,14.4a3.439,3.439,0,1,1,3.439,3.379A3.41,3.41,0,0,1,13.122,14.4ZM1.508,15.921a1.482,1.482,0,1,1,0-2.963H8.083a1.482,1.482,0,1,1,0,2.963ZM0,3.379A3.409,3.409,0,0,1,3.439,0,3.409,3.409,0,0,1,6.878,3.379,3.409,3.409,0,0,1,3.439,6.758,3.41,3.41,0,0,1,0,3.379ZM11.918,4.86a1.481,1.481,0,1,1,0-2.962h6.575a1.481,1.481,0,1,1,0,2.962Z\"\n      transform=\"translate(0 0)\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g transform=\"translate(2 2.5)\">\n    <path\n      d=\"M7.234.588H0\"\n      transform=\"translate(0.883 14.898)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M5.76,2.88A2.88,2.88,0,1,1,2.88,0,2.88,2.88,0,0,1,5.76,2.88Z\"\n      transform=\"translate(13.358 12.607)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M0,.588H7.235\"\n      transform=\"translate(11.883 3.174)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M0,2.88A2.88,2.88,0,1,0,2.88,0,2.879,2.879,0,0,0,0,2.88Z\"\n      transform=\"translate(0.883 0.882)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n  </g>\n);\n\ninterface IFilterProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst Filter = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IFilterProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nFilter.displayName = 'FilterIcon';\n\nexport default Filter;\n"
  },
  {
    "path": "app/assets/icons/FlipIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M24 6V42\"\n      stroke={color}\n      strokeWidth={1.5}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M4 34L16 12V34H4Z\"\n      fill={color}\n      stroke={color}\n      strokeWidth={1.5}\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M44 34H32V12L44 34Z\"\n      fill={color}\n      stroke={color}\n      strokeWidth={1.5}\n      strokeLinejoin=\"round\"\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g>\n    <path\n      d=\"M24 6V42\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M4 34L16 12V34H4Z\"\n      fill=\"none\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M44 34H32V12L44 34Z\"\n      fill=\"none\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n  </g>\n);\n\ninterface IFlipProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst Flip = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IFlipProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 48 48\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={2} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 48 48\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nFlip.displayName = 'FlipIcon';\n\nexport default Flip;\n"
  },
  {
    "path": "app/assets/icons/GlobalIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M7.64859 20.9098C7.61859 20.9098 7.57859 20.9298 7.54859 20.9298C5.60859 19.9698 4.02859 18.3798 3.05859 16.4398C3.05859 16.4098 3.07859 16.3698 3.07859 16.3398C4.29859 16.6998 5.55859 16.9698 6.80859 17.1798C7.02859 18.4398 7.28859 19.6898 7.64859 20.9098Z\"\n      fill={color}\n    />\n    <path\n      d=\"M20.9391 16.4498C19.9491 18.4398 18.2991 20.0498 16.2891 21.0198C16.6691 19.7498 16.9891 18.4698 17.1991 17.1798C18.4591 16.9698 19.6991 16.6998 20.9191 16.3398C20.9091 16.3798 20.9391 16.4198 20.9391 16.4498Z\"\n      fill={color}\n    />\n    <path\n      d=\"M21.0191 7.71047C19.7591 7.33047 18.4891 7.02047 17.1991 6.80047C16.9891 5.51047 16.6791 4.23047 16.2891 2.98047C18.3591 3.97047 20.0291 5.64047 21.0191 7.71047Z\"\n      fill={color}\n    />\n    <path\n      d=\"M7.65047 3.08859C7.29047 4.30859 7.03047 5.54859 6.82047 6.80859C5.53047 7.00859 4.25047 7.32859 2.98047 7.70859C3.95047 5.69859 5.56047 4.04859 7.55047 3.05859C7.58047 3.05859 7.62047 3.08859 7.65047 3.08859Z\"\n      fill={color}\n    />\n    <path\n      d=\"M15.4917 6.59C13.1717 6.33 10.8317 6.33 8.51172 6.59C8.76172 5.22 9.08172 3.85 9.53172 2.53C9.55172 2.45 9.54172 2.39 9.55172 2.31C10.3417 2.12 11.1517 2 12.0017 2C12.8417 2 13.6617 2.12 14.4417 2.31C14.4517 2.39 14.4517 2.45 14.4717 2.53C14.9217 3.86 15.2417 5.22 15.4917 6.59Z\"\n      fill={color}\n    />\n    <path\n      d=\"M6.59 15.4917C5.21 15.2417 3.85 14.9217 2.53 14.4717C2.45 14.4517 2.39 14.4617 2.31 14.4517C2.12 13.6617 2 12.8517 2 12.0017C2 11.1617 2.12 10.3417 2.31 9.56172C2.39 9.55172 2.45 9.55172 2.53 9.53172C3.86 9.09172 5.21 8.76172 6.59 8.51172C6.34 10.8317 6.34 13.1717 6.59 15.4917Z\"\n      fill={color}\n    />\n    <path\n      d=\"M22.0002 12.0017C22.0002 12.8517 21.8802 13.6617 21.6902 14.4517C21.6102 14.4617 21.5502 14.4517 21.4702 14.4717C20.1402 14.9117 18.7802 15.2417 17.4102 15.4917C17.6702 13.1717 17.6702 10.8317 17.4102 8.51172C18.7802 8.76172 20.1502 9.08172 21.4702 9.53172C21.5502 9.55172 21.6102 9.56172 21.6902 9.56172C21.8802 10.3517 22.0002 11.1617 22.0002 12.0017Z\"\n      fill={color}\n    />\n    <path\n      d=\"M15.4917 17.4102C15.2417 18.7902 14.9217 20.1502 14.4717 21.4702C14.4517 21.5502 14.4517 21.6102 14.4417 21.6902C13.6617 21.8802 12.8417 22.0002 12.0017 22.0002C11.1517 22.0002 10.3417 21.8802 9.55172 21.6902C9.54172 21.6102 9.55172 21.5502 9.53172 21.4702C9.09172 20.1402 8.76172 18.7902 8.51172 17.4102C9.67172 17.5402 10.8317 17.6302 12.0017 17.6302C13.1717 17.6302 14.3417 17.5402 15.4917 17.4102Z\"\n      fill={color}\n    />\n    <path\n      d=\"M15.7633 15.7633C13.2622 16.0789 10.7378 16.0789 8.23667 15.7633C7.92111 13.2622 7.92111 10.7378 8.23667 8.23667C10.7378 7.92111 13.2622 7.92111 15.7633 8.23667C16.0789 10.7378 16.0789 13.2622 15.7633 15.7633Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M12 22.75C6.07 22.75 1.25 17.93 1.25 12C1.25 6.07 6.07 1.25 12 1.25C17.93 1.25 22.75 6.07 22.75 12C22.75 17.93 17.93 22.75 12 22.75ZM12 2.75C6.9 2.75 2.75 6.9 2.75 12C2.75 17.1 6.9 21.25 12 21.25C17.1 21.25 21.25 17.1 21.25 12C21.25 6.9 17.1 2.75 12 2.75Z\"\n      fill={color}\n    />\n    <path\n      d=\"M8.99828 21.75H7.99828C7.58828 21.75 7.24828 21.41 7.24828 21C7.24828 20.59 7.56828 20.26 7.97828 20.25C6.40828 14.89 6.40828 9.11 7.97828 3.75C7.56828 3.74 7.24828 3.41 7.24828 3C7.24828 2.59 7.58828 2.25 7.99828 2.25H8.99828C9.23828 2.25 9.46828 2.37 9.60828 2.56C9.74828 2.76 9.78828 3.01 9.70828 3.24C7.82828 8.89 7.82828 15.11 9.70828 20.77C9.78828 21 9.74828 21.25 9.60828 21.45C9.46828 21.63 9.23828 21.75 8.99828 21.75Z\"\n      fill={color}\n    />\n    <path\n      d=\"M14.9984 21.7507C14.9184 21.7507 14.8384 21.7407 14.7584 21.7107C14.3684 21.5807 14.1484 21.1507 14.2884 20.7607C16.1684 15.1107 16.1684 8.89067 14.2884 3.23067C14.1584 2.84067 14.3684 2.41067 14.7584 2.28067C15.1584 2.15067 15.5784 2.36067 15.7084 2.75067C17.6984 8.71067 17.6984 15.2707 15.7084 21.2207C15.6084 21.5507 15.3084 21.7507 14.9984 21.7507Z\"\n      fill={color}\n    />\n    <path\n      d=\"M12 17.2008C9.21 17.2008 6.43 16.8108 3.75 16.0208C3.74 16.4208 3.41 16.7508 3 16.7508C2.59 16.7508 2.25 16.4108 2.25 16.0008V15.0008C2.25 14.7608 2.37 14.5308 2.56 14.3908C2.76 14.2508 3.01 14.2108 3.24 14.2908C8.89 16.1708 15.12 16.1708 20.77 14.2908C21 14.2108 21.25 14.2508 21.45 14.3908C21.65 14.5308 21.76 14.7608 21.76 15.0008V16.0008C21.76 16.4108 21.42 16.7508 21.01 16.7508C20.6 16.7508 20.27 16.4308 20.26 16.0208C17.57 16.8108 14.79 17.2008 12 17.2008Z\"\n      fill={color}\n    />\n    <path\n      d=\"M20.9998 9.74938C20.9198 9.74938 20.8398 9.73938 20.7598 9.70938C15.1098 7.82938 8.87978 7.82938 3.22978 9.70938C2.82978 9.83938 2.40978 9.62937 2.27978 9.23937C2.15978 8.83937 2.36978 8.41938 2.75978 8.28938C8.71978 6.29938 15.2798 6.29938 21.2298 8.28938C21.6198 8.41938 21.8398 8.84937 21.6998 9.23937C21.6098 9.54937 21.3098 9.74938 20.9998 9.74938Z\"\n      fill={color}\n    />\n  </g>\n);\n\ninterface IGlobalProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst Global = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IGlobalProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nGlobal.displayName = 'GlobalIcon';\n\nexport default Global;\n"
  },
  {
    "path": "app/assets/icons/HistoryIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <path\n    fill={color}\n    d=\"M19.25 12A7.25 7.25 0 0 0 7.584 6.25h.666a1 1 0 0 1 0 2h-3a1 1 0 0 1-1-1V7h-.034l.034-.052V4.25a1 1 0 0 1 2 0v.504A9.21 9.21 0 0 1 12 2.75a9.25 9.25 0 1 1-9.182 8.12c.063-.512.516-.87 1.032-.87c.59 0 1.017.569.949 1.156A7.25 7.25 0 1 0 19.25 12ZM13 8a1 1 0 1 0-2 0v5a1 1 0 0 0 1 1h3a1 1 0 1 0 0-2h-2V8Z\"\n  />\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <path\n    fill={color}\n    d=\"M19.5 12A7.5 7.5 0 0 0 6.9 6.5h1.35a.75.75 0 0 1 0 1.5h-3a.75.75 0 0 1-.75-.75v-3a.75.75 0 0 1 1.5 0v1.042a9 9 0 1 1-2.895 5.331a.749.749 0 0 1 .752-.623c.46 0 .791.438.724.892A7.5 7.5 0 1 0 19.5 12Zm-7-4.25a.75.75 0 0 0-1.5 0v4.5c0 .414.336.75.75.75h2.5a.75.75 0 0 0 0-1.5H12.5V7.75Z\"\n  />\n);\n\ninterface IHistoryProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The class name of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst History = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: IHistoryProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nHistory.displayName = 'HistoryIcon';\n\nexport default History;\n"
  },
  {
    "path": "app/assets/icons/HomeIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(2.5 2)\">\n    <path\n      d=\"M6.635,18.773V15.716A1.419,1.419,0,0,1,8.058,14.3h2.874a1.429,1.429,0,0,1,1.007.414,1.408,1.408,0,0,1,.417,1v3.058a1.213,1.213,0,0,0,.356.867,1.231,1.231,0,0,0,.871.36h1.961a3.46,3.46,0,0,0,2.443-1A3.41,3.41,0,0,0,19,16.578V7.867a2.473,2.473,0,0,0-.9-1.9L11.434.676A3.1,3.1,0,0,0,7.485.747L.967,5.965A2.474,2.474,0,0,0,0,7.867v8.7A3.444,3.444,0,0,0,3.456,20H5.372a1.231,1.231,0,0,0,1.236-1.218Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g transform=\"translate(2.5 2)\">\n    <path\n      d=\"M6.657,18.771V15.7a1.426,1.426,0,0,1,1.424-1.419h2.886A1.426,1.426,0,0,1,12.4,15.7h0v3.076A1.225,1.225,0,0,0,13.6,20h1.924A3.456,3.456,0,0,0,19,16.562h0V7.838a2.439,2.439,0,0,0-.962-1.9L11.458.685a3.18,3.18,0,0,0-3.944,0L.962,5.943A2.42,2.42,0,0,0,0,7.847v8.714A3.456,3.456,0,0,0,3.473,20H5.4a1.235,1.235,0,0,0,1.241-1.229h0\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n  </g>\n);\n\ninterface IHomeProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst Home = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: IHomeProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nHome.displayName = 'HomeIcon';\n\nexport default Home;\n"
  },
  {
    "path": "app/assets/icons/InfoIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(2 1.999)\">\n    <path\n      d=\"M10,20a10,10,0,1,1,7.074-2.929A10.011,10.011,0,0,1,10,20Zm0-7.069a.871.871,0,0,0-.87.869.875.875,0,1,0,.87-.869Zm0-7.6a.892.892,0,0,0-.88.88v4.42a.875.875,0,0,0,1.751,0V6.21A.876.876,0,0,0,10,5.33Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g transform=\"translate(2 2)\">\n    <path\n      d=\"M9.25,0A9.25,9.25,0,1,1,0,9.25,9.25,9.25,0,0,1,9.25,0Z\"\n      transform=\"translate(0.75 0.75)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M.5,0V4.419\"\n      transform=\"translate(9.495 6.204)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M.5.5H.5\"\n      transform=\"translate(9.5 13.296)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n  </g>\n);\n\ninterface IInfoProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst Info = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: IInfoProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nInfo.displayName = 'InfoIcon';\n\nexport default Info;\n"
  },
  {
    "path": "app/assets/icons/LibraryIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <path\n    fill={color}\n    d=\"M3 6.25A3.25 3.25 0 0 1 6.25 3h9a3.25 3.25 0 0 1 3.25 3.25v9c0 .646-.189 1.248-.514 1.755l-5.692-5.376a2.25 2.25 0 0 0-3.09 0l-5.69 5.375A3.235 3.235 0 0 1 3 15.25v-9Zm10.747 2.746a1.248 1.248 0 1 0 0-2.496a1.248 1.248 0 0 0 0 2.496Zm-2.483 3.724l5.642 5.327a3.235 3.235 0 0 1-1.656.453h-9a3.235 3.235 0 0 1-1.656-.453l5.64-5.327a.75.75 0 0 1 1.03 0ZM8.75 21a3.247 3.247 0 0 1-2.74-1.5h9.74a3.75 3.75 0 0 0 3.75-3.75V6.011a3.247 3.247 0 0 1 1.5 2.74v7a5.25 5.25 0 0 1-5.25 5.25h-7Z\"\n  />\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <path\n    fill={color}\n    d=\"M13.748 8.996a1.248 1.248 0 1 0 0-2.496a1.248 1.248 0 0 0 0 2.496ZM6.25 3A3.25 3.25 0 0 0 3 6.25v9a3.25 3.25 0 0 0 3.25 3.25h9a3.25 3.25 0 0 0 3.25-3.25v-9A3.25 3.25 0 0 0 15.25 3h-9ZM4.5 6.25c0-.966.784-1.75 1.75-1.75h9c.966 0 1.75.784 1.75 1.75v9c0 .231-.045.452-.126.654l-4.587-4.291a2.25 2.25 0 0 0-3.074 0l-4.587 4.29a1.745 1.745 0 0 1-.126-.653v-9Zm6.762 6.458l4.505 4.214c-.163.05-.337.078-.517.078h-9a1.73 1.73 0 0 1-.517-.078l4.504-4.214a.75.75 0 0 1 1.025 0ZM8.75 21a3.247 3.247 0 0 1-2.74-1.5h9.74a3.75 3.75 0 0 0 3.75-3.75V6.011a3.248 3.248 0 0 1 1.5 2.74v7A5.25 5.25 0 0 1 15.75 21h-7Z\"\n  />\n);\n\ninterface ILibraryProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst Library = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: ILibraryProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nLibrary.displayName = 'LibraryIcon';\n\nexport default Library;\n"
  },
  {
    "path": "app/assets/icons/LogInIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M16.8 2H14.2C11 2 9 4 9 7.2V11.25H13.44L11.37 9.18C11.22 9.03 11.15 8.84 11.15 8.65C11.15 8.46 11.22 8.27 11.37 8.12C11.66 7.83 12.14 7.83 12.43 8.12L15.78 11.47C16.07 11.76 16.07 12.24 15.78 12.53L12.43 15.88C12.14 16.17 11.66 16.17 11.37 15.88C11.08 15.59 11.08 15.11 11.37 14.82L13.44 12.75H9V16.8C9 20 11 22 14.2 22H16.79C19.99 22 21.99 20 21.99 16.8V7.2C22 4 20 2 16.8 2Z\"\n      fill={color}\n    />\n    <path\n      d=\"M2.75 11.25C2.34 11.25 2 11.59 2 12C2 12.41 2.34 12.75 2.75 12.75H9V11.25H2.75Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M15.2405 22.2705H15.1105C10.6705 22.2705 8.5305 20.5205 8.1605 16.6005C8.1205 16.1905 8.4205 15.8205 8.8405 15.7805C9.2505 15.7405 9.6205 16.0505 9.6605 16.4605C9.9505 19.6005 11.4305 20.7705 15.1205 20.7705H15.2505C19.3205 20.7705 20.7605 19.3305 20.7605 15.2605V8.74047C20.7605 4.67047 19.3205 3.23047 15.2505 3.23047H15.1205C11.4105 3.23047 9.9305 4.42047 9.6605 7.62047C9.6105 8.03047 9.2705 8.34047 8.8405 8.30047C8.4205 8.27047 8.1205 7.90047 8.1505 7.49047C8.4905 3.51047 10.6405 1.73047 15.1105 1.73047H15.2405C20.1505 1.73047 22.2505 3.83047 22.2505 8.74047V15.2605C22.2505 20.1705 20.1505 22.2705 15.2405 22.2705Z\"\n      fill={color}\n    />\n    <path\n      d=\"M14.88 12.75H2C1.59 12.75 1.25 12.41 1.25 12C1.25 11.59 1.59 11.25 2 11.25H14.88C15.29 11.25 15.63 11.59 15.63 12C15.63 12.41 15.3 12.75 14.88 12.75Z\"\n      fill={color}\n    />\n    <path\n      d=\"M12.6498 16.0998C12.4598 16.0998 12.2698 16.0298 12.1198 15.8798C11.8298 15.5898 11.8298 15.1098 12.1198 14.8198L14.9398 11.9998L12.1198 9.17984C11.8298 8.88984 11.8298 8.40984 12.1198 8.11984C12.4098 7.82984 12.8898 7.82984 13.1798 8.11984L16.5298 11.4698C16.8198 11.7598 16.8198 12.2398 16.5298 12.5298L13.1798 15.8798C13.0298 16.0298 12.8398 16.0998 12.6498 16.0998Z\"\n      fill={color}\n    />\n  </g>\n);\n\ninterface ILogInProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst LogIn = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: ILogInProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nLogIn.displayName = 'LogInIcon';\n\nexport default LogIn;\n"
  },
  {
    "path": "app/assets/icons/LogOutIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M16.8 2H14.2C11 2 9 4 9 7.2V11.25H15.25C15.66 11.25 16 11.59 16 12C16 12.41 15.66 12.75 15.25 12.75H9V16.8C9 20 11 22 14.2 22H16.79C19.99 22 21.99 20 21.99 16.8V7.2C22 4 20 2 16.8 2Z\"\n      fill={color}\n    />\n    <path\n      d=\"M4.56141 11.2498L6.63141 9.17984C6.78141 9.02984 6.85141 8.83984 6.85141 8.64984C6.85141 8.45984 6.78141 8.25984 6.63141 8.11984C6.34141 7.82984 5.86141 7.82984 5.57141 8.11984L2.22141 11.4698C1.93141 11.7598 1.93141 12.2398 2.22141 12.5298L5.57141 15.8798C5.86141 16.1698 6.34141 16.1698 6.63141 15.8798C6.92141 15.5898 6.92141 15.1098 6.63141 14.8198L4.56141 12.7498H9.00141V11.2498H4.56141Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M15.2405 22.2705H15.1105C10.6705 22.2705 8.5305 20.5205 8.1605 16.6005C8.1205 16.1905 8.4205 15.8205 8.8405 15.7805C9.2405 15.7405 9.6205 16.0505 9.6605 16.4605C9.9505 19.6005 11.4305 20.7705 15.1205 20.7705H15.2505C19.3205 20.7705 20.7605 19.3305 20.7605 15.2605V8.74047C20.7605 4.67047 19.3205 3.23047 15.2505 3.23047H15.1205C11.4105 3.23047 9.9305 4.42047 9.6605 7.62047C9.6105 8.03047 9.2605 8.34047 8.8405 8.30047C8.4205 8.27047 8.1205 7.90047 8.1505 7.49047C8.4905 3.51047 10.6405 1.73047 15.1105 1.73047H15.2405C20.1505 1.73047 22.2505 3.83047 22.2505 8.74047V15.2605C22.2505 20.1705 20.1505 22.2705 15.2405 22.2705Z\"\n      fill={color}\n    />\n    <path\n      d=\"M15.0011 12.75H3.62109C3.21109 12.75 2.87109 12.41 2.87109 12C2.87109 11.59 3.21109 11.25 3.62109 11.25H15.0011C15.4111 11.25 15.7511 11.59 15.7511 12C15.7511 12.41 15.4111 12.75 15.0011 12.75Z\"\n      fill={color}\n    />\n    <path\n      d=\"M5.85141 16.0998C5.66141 16.0998 5.47141 16.0298 5.32141 15.8798L1.97141 12.5298C1.68141 12.2398 1.68141 11.7598 1.97141 11.4698L5.32141 8.11984C5.61141 7.82984 6.09141 7.82984 6.38141 8.11984C6.67141 8.40984 6.67141 8.88984 6.38141 9.17984L3.56141 11.9998L6.38141 14.8198C6.67141 15.1098 6.67141 15.5898 6.38141 15.8798C6.24141 16.0298 6.04141 16.0998 5.85141 16.0998Z\"\n      fill={color}\n    />\n  </g>\n);\n\ninterface ILogOutProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst LogOut = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: ILogOutProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nLogOut.displayName = 'LogOutIcon';\n\nexport default LogOut;\n"
  },
  {
    "path": "app/assets/icons/MailEditIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M17 3H7C4 3 2 4.5 2 8V15C2 18.5 4 20 7 20H10.57C11.16 20 11.64 19.48 11.56 18.89C11.46 18.18 11.48 17.44 11.63 16.68C12.16 14.08 14.3 12.01 16.92 11.58C18.23 11.37 19.48 11.55 20.58 12.03C21.25 12.32 22 11.86 22 11.13V8C22 4.5 20 3 17 3ZM17.47 8.59L14.34 11.09C13.68 11.62 12.84 11.88 12 11.88C11.16 11.88 10.31 11.62 9.66 11.09L6.53 8.59C6.21 8.33 6.16 7.86 6.41 7.53C6.67 7.21 7.14 7.15 7.46 7.41L10.59 9.91C11.35 10.52 12.64 10.52 13.4 9.91L16.53 7.41C16.85 7.15 17.33 7.2 17.58 7.53C17.84 7.86 17.79 8.33 17.47 8.59Z\"\n      fill={color}\n    />\n    <path\n      d=\"M18 13C15.24 13 13 15.23 13 18C13 20.76 15.24 23 18 23C20.77 23 23 20.76 23 18C23 15.23 20.77 13 18 13ZM20.05 17.55L19.68 17.91L17.5 20.1C17.4 20.19 17.21 20.29 17.07 20.31L16.09 20.45C15.74 20.5 15.49 20.25 15.54 19.9L15.68 18.92C15.7 18.78 15.79 18.59 15.89 18.49L18.07 16.32L18.43 15.95C18.67 15.71 18.94 15.55 19.22 15.55C19.46 15.55 19.73 15.66 20.03 15.95C20.7 16.6 20.49 17.11 20.05 17.55Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M12 21.25H7C3.35 21.25 1.25 19.15 1.25 15.5V8.5C1.25 4.85 3.35 2.75 7 2.75H17C20.65 2.75 22.75 4.85 22.75 8.5V11.5C22.75 11.91 22.41 12.25 22 12.25C21.59 12.25 21.25 11.91 21.25 11.5V8.5C21.25 5.64 19.86 4.25 17 4.25H7C4.14 4.25 2.75 5.64 2.75 8.5V15.5C2.75 18.36 4.14 19.75 7 19.75H12C12.41 19.75 12.75 20.09 12.75 20.5C12.75 20.91 12.41 21.25 12 21.25Z\"\n      fill={color}\n    />\n    <path\n      d=\"M11.9988 12.868C11.1588 12.868 10.3088 12.608 9.6588 12.078L6.5288 9.578C6.2088 9.318 6.14881 8.84802 6.4088 8.52802C6.6688 8.20802 7.13879 8.15802 7.45879 8.40802L10.5888 10.908C11.3488 11.518 12.6388 11.518 13.3988 10.908L16.5288 8.40802C16.8488 8.14802 17.3188 8.19802 17.5788 8.52802C17.8388 8.84802 17.7888 9.328 17.4588 9.578L14.3288 12.078C13.6888 12.608 12.8388 12.868 11.9988 12.868Z\"\n      fill={color}\n    />\n    <path\n      d=\"M15.8196 21.7816C15.4396 21.7816 15.0796 21.6416 14.8196 21.3816C14.5096 21.0716 14.3696 20.6216 14.4396 20.1516L14.6296 18.8016C14.6796 18.4516 14.8896 18.0316 15.1396 17.7816L18.6796 14.2416C19.1596 13.7616 19.6296 13.5116 20.1396 13.4616C20.7596 13.4016 21.3796 13.6616 21.9596 14.2416C22.5396 14.8216 22.7996 15.4316 22.7396 16.0616C22.6896 16.5616 22.4296 17.0416 21.9596 17.5216L18.4196 21.0616C18.1696 21.3116 17.7496 21.5216 17.3996 21.5716L16.0495 21.7616C15.9695 21.7716 15.8996 21.7816 15.8196 21.7816ZM20.3096 14.9516C20.2996 14.9516 20.2896 14.9516 20.2796 14.9516C20.1396 14.9616 19.9496 15.0916 19.7396 15.3016L16.1996 18.8416C16.1696 18.8716 16.1196 18.9716 16.1196 19.0116L15.9396 20.2616L17.1896 20.0816C17.2296 20.0716 17.3295 20.0216 17.3595 19.9916L20.8996 16.4516C21.1096 16.2316 21.2396 16.0516 21.2496 15.9116C21.2696 15.7116 21.0696 15.4716 20.8996 15.3016C20.7396 15.1416 20.5096 14.9516 20.3096 14.9516Z\"\n      fill={color}\n    />\n    <path\n      d=\"M20.9206 18.2509C20.8506 18.2509 20.7806 18.2409 20.7206 18.2209C19.4006 17.8509 18.3506 16.8009 17.9806 15.4809C17.8706 15.0809 18.1006 14.6709 18.5006 14.5509C18.9006 14.4409 19.3106 14.6709 19.4206 15.0709C19.6506 15.8909 20.3006 16.5409 21.1206 16.7709C21.5206 16.8809 21.7506 17.3009 21.6406 17.7009C21.5506 18.0309 21.2506 18.2509 20.9206 18.2509Z\"\n      fill={color}\n    />\n  </g>\n);\n\ninterface IMailEditProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst MailEdit = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IMailEditProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nMailEdit.displayName = 'MailEditIcon';\n\nexport default MailEdit;\n"
  },
  {
    "path": "app/assets/icons/MenuIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M16.19 2H7.81C4.17 2 2 4.17 2 7.81V16.18C2 19.83 4.17 22 7.81 22H16.18C19.82 22 21.99 19.83 21.99 16.19V7.81C22 4.17 19.83 2 16.19 2ZM17 17.25H7C6.59 17.25 6.25 16.91 6.25 16.5C6.25 16.09 6.59 15.75 7 15.75H17C17.41 15.75 17.75 16.09 17.75 16.5C17.75 16.91 17.41 17.25 17 17.25ZM17 12.75H7C6.59 12.75 6.25 12.41 6.25 12C6.25 11.59 6.59 11.25 7 11.25H17C17.41 11.25 17.75 11.59 17.75 12C17.75 12.41 17.41 12.75 17 12.75ZM17 8.25H7C6.59 8.25 6.25 7.91 6.25 7.5C6.25 7.09 6.59 6.75 7 6.75H17C17.41 6.75 17.75 7.09 17.75 7.5C17.75 7.91 17.41 8.25 17 8.25Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M21 7.75H3C2.59 7.75 2.25 7.41 2.25 7C2.25 6.59 2.59 6.25 3 6.25H21C21.41 6.25 21.75 6.59 21.75 7C21.75 7.41 21.41 7.75 21 7.75Z\"\n      fill={color}\n    />\n    <path\n      d=\"M21 12.75H3C2.59 12.75 2.25 12.41 2.25 12C2.25 11.59 2.59 11.25 3 11.25H21C21.41 11.25 21.75 11.59 21.75 12C21.75 12.41 21.41 12.75 21 12.75Z\"\n      fill={color}\n    />\n    <path\n      d=\"M21 17.75H3C2.59 17.75 2.25 17.41 2.25 17C2.25 16.59 2.59 16.25 3 16.25H21C21.41 16.25 21.75 16.59 21.75 17C21.75 17.41 21.41 17.75 21 17.75Z\"\n      fill={color}\n    />\n  </g>\n);\n\ninterface IMenuProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst Menu = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IMenuProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nMenu.displayName = 'MenuIcon';\n\nexport default Menu;\n"
  },
  {
    "path": "app/assets/icons/MoonIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M21.5287 15.9294C21.3687 15.6594 20.9187 15.2394 19.7987 15.4394C19.1787 15.5494 18.5487 15.5994 17.9187 15.5694C15.5887 15.4694 13.4787 14.3994 12.0087 12.7494C10.7087 11.2994 9.90873 9.40938 9.89873 7.36938C9.89873 6.22938 10.1187 5.12938 10.5687 4.08938C11.0087 3.07938 10.6987 2.54938 10.4787 2.32938C10.2487 2.09938 9.70873 1.77938 8.64873 2.21938C4.55873 3.93938 2.02873 8.03938 2.32873 12.4294C2.62873 16.5594 5.52873 20.0894 9.36873 21.4194C10.2887 21.7394 11.2587 21.9294 12.2587 21.9694C12.4187 21.9794 12.5787 21.9894 12.7387 21.9894C16.0887 21.9894 19.2287 20.4094 21.2087 17.7194C21.8787 16.7894 21.6987 16.1994 21.5287 15.9294Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M12.4585 22.7482C12.2885 22.7482 12.1185 22.7482 11.9485 22.7382C6.34848 22.4882 1.66848 17.9782 1.27848 12.4782C0.938483 7.75816 3.66848 3.34816 8.06848 1.49816C9.31848 0.978161 9.97848 1.37816 10.2585 1.66816C10.5385 1.94816 10.9285 2.59816 10.4085 3.78816C9.94848 4.84816 9.71848 5.97816 9.72848 7.13816C9.74848 11.5682 13.4285 15.3282 17.9185 15.5082C18.5685 15.5382 19.2085 15.4882 19.8285 15.3782C21.1485 15.1382 21.6985 15.6682 21.9085 16.0082C22.1185 16.3482 22.3585 17.0782 21.5585 18.1582C19.4385 21.0582 16.0685 22.7482 12.4585 22.7482ZM2.76848 12.3682C3.10848 17.1282 7.16848 21.0282 12.0085 21.2382C15.2985 21.3982 18.4185 19.8982 20.3385 17.2782C20.4885 17.0682 20.5585 16.9182 20.5885 16.8382C20.4985 16.8282 20.3385 16.8182 20.0885 16.8682C19.3585 16.9982 18.5985 17.0482 17.8485 17.0182C12.5685 16.8082 8.24848 12.3782 8.21848 7.15816C8.21848 5.77816 8.48848 4.44816 9.03848 3.19816C9.13848 2.97816 9.15848 2.82816 9.16848 2.74816C9.07848 2.74816 8.91848 2.76816 8.65848 2.87816C4.84848 4.47816 2.48848 8.29816 2.76848 12.3682Z\"\n      fill={color}\n    />\n  </g>\n);\n\ninterface IMoonProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst Moon = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: IMoonProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nMoon.displayName = 'MoonIcon';\n\nexport default Moon;\n"
  },
  {
    "path": "app/assets/icons/MovieIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M24 44C35.0457 44 44 35.0457 44 24C44 12.9543 35.0457 4 24 4C12.9543 4 4 12.9543 4 24C4 35.0457 12.9543 44 24 44Z\"\n      fill={color}\n      stroke={color}\n      strokeWidth=\"2\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M24 18C25.6569 18 27 16.6569 27 15C27 13.3431 25.6569 12 24 12C22.3431 12 21 13.3431 21 15C21 16.6569 22.3431 18 24 18Z\"\n      fill=\"#FFF\"\n      stroke=\"#FFF\"\n      strokeWidth=\"2\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M24 36C25.6569 36 27 34.6569 27 33C27 31.3431 25.6569 30 24 30C22.3431 30 21 31.3431 21 33C21 34.6569 22.3431 36 24 36Z\"\n      fill=\"#FFF\"\n      stroke=\"#FFF\"\n      strokeWidth=\"2\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M15 27C16.6569 27 18 25.6569 18 24C18 22.3431 16.6569 21 15 21C13.3431 21 12 22.3431 12 24C12 25.6569 13.3431 27 15 27Z\"\n      fill=\"#FFF\"\n      stroke=\"#FFF\"\n      strokeWidth=\"2\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M33 27C34.6569 27 36 25.6569 36 24C36 22.3431 34.6569 21 33 21C31.3431 21 30 22.3431 30 24C30 25.6569 31.3431 27 33 27Z\"\n      fill=\"#FFF\"\n      stroke=\"#FFF\"\n      strokeWidth=\"2\"\n      strokeLinejoin=\"round\"\n    />\n    <path d=\"M24 44H44\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g>\n    <path\n      d=\"M24 44C35.0457 44 44 35.0457 44 24C44 12.9543 35.0457 4 24 4C12.9543 4 4 12.9543 4 24C4 35.0457 12.9543 44 24 44Z\"\n      fill=\"none\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M24 18C25.6569 18 27 16.6569 27 15C27 13.3431 25.6569 12 24 12C22.3431 12 21 13.3431 21 15C21 16.6569 22.3431 18 24 18Z\"\n      fill=\"none\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M24 36C25.6569 36 27 34.6569 27 33C27 31.3431 25.6569 30 24 30C22.3431 30 21 31.3431 21 33C21 34.6569 22.3431 36 24 36Z\"\n      fill=\"none\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M15 27C16.6569 27 18 25.6569 18 24C18 22.3431 16.6569 21 15 21C13.3431 21 12 22.3431 12 24C12 25.6569 13.3431 27 15 27Z\"\n      fill=\"none\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M33 27C34.6569 27 36 25.6569 36 24C36 22.3431 34.6569 21 33 21C31.3431 21 30 22.3431 30 24C30 25.6569 31.3431 27 33 27Z\"\n      fill=\"none\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <path d=\"M24 44H44\" stroke={color} strokeWidth={strokeWidth} strokeLinecap=\"round\" />\n  </g>\n);\n\ninterface IMovieProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst Movie = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: IMovieProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 48 48\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={3} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 48 48\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nMovie.displayName = 'MovieIcon';\n\nexport default Movie;\n"
  },
  {
    "path": "app/assets/icons/NextIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M3.76172 7.21957V16.7896C3.76172 18.7496 5.89172 19.9796 7.59172 18.9996L11.7417 16.6096L15.8917 14.2096C17.5917 13.2296 17.5917 10.7796 15.8917 9.79957L11.7417 7.39957L7.59172 5.00957C5.89172 4.02957 3.76172 5.24957 3.76172 7.21957Z\"\n      fill={color}\n    />\n    <path\n      d=\"M20.2383 18.9303C19.8283 18.9303 19.4883 18.5903 19.4883 18.1803V5.82031C19.4883 5.41031 19.8283 5.07031 20.2383 5.07031C20.6483 5.07031 20.9883 5.41031 20.9883 5.82031V18.1803C20.9883 18.5903 20.6583 18.9303 20.2383 18.9303Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M6.3078 20.0884C5.7378 20.0884 5.17781 19.9384 4.65781 19.6384C3.61781 19.0384 3.00781 17.9784 3.00781 16.7784V7.20842C3.00781 6.01842 3.62781 4.94844 4.65781 4.34844C5.69781 3.74844 6.92779 3.74844 7.95779 4.34844L16.2478 9.12844C17.2778 9.72844 17.8978 10.7984 17.8978 11.9884C17.8978 13.1784 17.2778 14.2484 16.2478 14.8484L7.95779 19.6284C7.43779 19.9384 6.8778 20.0884 6.3078 20.0884ZM6.3078 5.40844C5.9978 5.40844 5.68781 5.48843 5.40781 5.64843C4.84781 5.97843 4.50781 6.55842 4.50781 7.20842V16.7784C4.50781 17.4284 4.84781 18.0084 5.40781 18.3384C5.96781 18.6584 6.64779 18.6684 7.20779 18.3384L15.4978 13.5584C16.0578 13.2284 16.3978 12.6484 16.3978 11.9984C16.3978 11.3484 16.0578 10.7684 15.4978 10.4384L7.20779 5.65844C6.92779 5.49844 6.6178 5.40844 6.3078 5.40844Z\"\n      fill={color}\n    />\n    <path\n      d=\"M20.2422 18.9303C19.8322 18.9303 19.4922 18.5903 19.4922 18.1803V5.82031C19.4922 5.41031 19.8322 5.07031 20.2422 5.07031C20.6522 5.07031 20.9922 5.41031 20.9922 5.82031V18.1803C20.9922 18.5903 20.6622 18.9303 20.2422 18.9303Z\"\n      fill={color}\n    />\n  </g>\n);\n\ninterface INextProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst Next = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: INextProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nNext.displayName = 'NextIcon';\n\nexport default Next;\n"
  },
  {
    "path": "app/assets/icons/PaperPlusIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(3.5 2)\">\n    <path\n      d=\"M4.674,20A4.7,4.7,0,0,1,0,15.29V4.51A4.493,4.493,0,0,1,4.465,0H9.752a.458.458,0,0,1,.455.46V3.68a3.341,3.341,0,0,0,3.308,3.34c.423,0,.794,0,1.122.006.257,0,.481,0,.68,0,.141,0,.323,0,.521,0,.229,0,.486-.005.716-.005A.448.448,0,0,1,17,7.47v8.04A4.478,4.478,0,0,1,12.544,20Zm.455-9.01a.742.742,0,0,0,.742.74h1.7V13.46a.738.738,0,1,0,1.475,0V11.73h1.713a.745.745,0,0,0,0-1.49H9.05V8.51a.738.738,0,1,0-1.475,0v1.73h-1.7A.747.747,0,0,0,5.129,10.99Zm8.518-5.431a2.017,2.017,0,0,1-2-2.017V.906a.472.472,0,0,1,.814-.334l3.986,4.187a.477.477,0,0,1-.339.807h-.645C14.824,5.568,14.161,5.565,13.647,5.559Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g transform=\"translate(3.5 2)\">\n    <path\n      d=\"M10.486,0H3.834A3.82,3.82,0,0,0,0,3.729V14.578a3.713,3.713,0,0,0,3.834,3.775h7.988a3.769,3.769,0,0,0,3.73-3.775v-9.3Z\"\n      transform=\"translate(0.75 0.762)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M0,0V2.909A2.575,2.575,0,0,0,2.569,5.484c1.316,0,2.663,0,2.754,0\"\n      transform=\"translate(10.974 0.75)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M4.9.5H0\"\n      transform=\"translate(5.893 10.414)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M.5,4.9V0\"\n      transform=\"translate(7.844 8.464)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n  </g>\n);\n\ninterface IPaperPlusProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst PaperPlus = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: IPaperPlusProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nPaperPlus.displayName = 'PaperPlusIcon';\n\nexport default PaperPlus;\n"
  },
  {
    "path": "app/assets/icons/Password.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M18.75 8V10.1C18.31 10.04 17.81 10.01 17.25 10V8C17.25 4.85 16.36 2.75 12 2.75C7.64 2.75 6.75 4.85 6.75 8V10C6.19 10.01 5.69 10.04 5.25 10.1V8C5.25 5.1 5.95 1.25 12 1.25C18.05 1.25 18.75 5.1 18.75 8Z\"\n      fill={color}\n    />\n    <path\n      d=\"M18.75 10.1C18.31 10.04 17.81 10.01 17.25 10H6.75C6.19 10.01 5.69 10.04 5.25 10.1C2.7 10.41 2 11.66 2 15V17C2 21 3 22 7 22H17C21 22 22 21 22 17V15C22 11.66 21.3 10.41 18.75 10.1ZM8.71 16.71C8.52 16.89 8.26 17 8 17C7.87 17 7.74 16.97 7.62 16.92C7.49 16.87 7.39 16.8 7.29 16.71C7.11 16.52 7 16.26 7 16C7 15.87 7.03 15.74 7.08 15.62C7.13 15.5 7.2 15.39 7.29 15.29C7.39 15.2 7.49 15.13 7.62 15.08C7.99 14.92 8.43 15.01 8.71 15.29C8.8 15.39 8.87 15.5 8.92 15.62C8.97 15.74 9 15.87 9 16C9 16.26 8.89 16.52 8.71 16.71ZM12.92 16.38C12.87 16.5 12.8 16.61 12.71 16.71C12.52 16.89 12.26 17 12 17C11.73 17 11.48 16.89 11.29 16.71C11.2 16.61 11.13 16.5 11.08 16.38C11.03 16.26 11 16.13 11 16C11 15.73 11.11 15.48 11.29 15.29C11.66 14.92 12.33 14.92 12.71 15.29C12.89 15.48 13 15.73 13 16C13 16.13 12.97 16.26 12.92 16.38ZM16.71 16.71C16.52 16.89 16.26 17 16 17C15.74 17 15.48 16.89 15.29 16.71C15.11 16.52 15 16.27 15 16C15 15.73 15.11 15.48 15.29 15.29C15.67 14.92 16.34 14.92 16.71 15.29C16.75 15.34 16.79 15.39 16.83 15.45C16.87 15.5 16.9 15.56 16.92 15.62C16.95 15.68 16.97 15.74 16.98 15.8C16.99 15.87 17 15.94 17 16C17 16.26 16.89 16.52 16.71 16.71Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M18 10.75C17.59 10.75 17.25 10.41 17.25 10V8C17.25 4.85 16.36 2.75 12 2.75C7.64 2.75 6.75 4.85 6.75 8V10C6.75 10.41 6.41 10.75 6 10.75C5.59 10.75 5.25 10.41 5.25 10V8C5.25 5.1 5.95 1.25 12 1.25C18.05 1.25 18.75 5.1 18.75 8V10C18.75 10.41 18.41 10.75 18 10.75Z\"\n      fill={color}\n    />\n    <path\n      d=\"M17 22.75H7C2.59 22.75 1.25 21.41 1.25 17V15C1.25 10.59 2.59 9.25 7 9.25H17C21.41 9.25 22.75 10.59 22.75 15V17C22.75 21.41 21.41 22.75 17 22.75ZM7 10.75C3.42 10.75 2.75 11.43 2.75 15V17C2.75 20.57 3.42 21.25 7 21.25H17C20.58 21.25 21.25 20.57 21.25 17V15C21.25 11.43 20.58 10.75 17 10.75H7Z\"\n      fill={color}\n    />\n    <path\n      d=\"M8 16.9989C7.87 16.9989 7.74 16.9689 7.62 16.9189C7.49 16.8689 7.39001 16.7989 7.29001 16.7089C7.11001 16.5189 7 16.2689 7 15.9989C7 15.8689 7.02999 15.7389 7.07999 15.6189C7.12999 15.4889 7.20001 15.3889 7.29001 15.2889C7.39001 15.1989 7.49 15.1289 7.62 15.0789C7.98 14.9189 8.42999 15.0089 8.70999 15.2889C8.79999 15.3889 8.87001 15.4989 8.92001 15.6189C8.97001 15.7389 9 15.8689 9 15.9989C9 16.2589 8.88999 16.5189 8.70999 16.7089C8.51999 16.8889 8.26 16.9989 8 16.9989Z\"\n      fill={color}\n    />\n    <path\n      d=\"M12 17.0009C11.74 17.0009 11.48 16.8909 11.29 16.7109C11.11 16.5209 11 16.2709 11 16.0009C11 15.8709 11.02 15.7409 11.08 15.6209C11.13 15.5009 11.2 15.3909 11.29 15.2909C11.52 15.0609 11.87 14.9509 12.19 15.0209C12.26 15.0309 12.32 15.0509 12.38 15.0809C12.44 15.1009 12.5 15.1309 12.56 15.1709C12.61 15.2009 12.66 15.2509 12.71 15.2909C12.8 15.3909 12.87 15.5009 12.92 15.6209C12.97 15.7409 13 15.8709 13 16.0009C13 16.2709 12.89 16.5209 12.71 16.7109C12.66 16.7509 12.61 16.7909 12.56 16.8309C12.5 16.8709 12.44 16.9009 12.38 16.9209C12.32 16.9509 12.26 16.9709 12.19 16.9809C12.13 16.9909 12.06 17.0009 12 17.0009Z\"\n      fill={color}\n    />\n    <path\n      d=\"M16 17.0009C15.73 17.0009 15.48 16.8909 15.29 16.7109C15.2 16.6109 15.13 16.5009 15.08 16.3809C15.03 16.2609 15 16.1309 15 16.0009C15 15.7409 15.11 15.4809 15.29 15.2909C15.34 15.2509 15.39 15.2109 15.44 15.1709C15.5 15.1309 15.56 15.1009 15.62 15.0809C15.68 15.0509 15.74 15.0309 15.8 15.0209C16.13 14.9509 16.47 15.0609 16.71 15.2909C16.89 15.4809 17 15.7309 17 16.0009C17 16.1309 16.97 16.2609 16.92 16.3809C16.87 16.5109 16.8 16.6109 16.71 16.7109C16.52 16.8909 16.26 17.0009 16 17.0009Z\"\n      fill={color}\n    />\n  </g>\n);\n\ninterface IPasswordProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst Password = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IPasswordProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nPassword.displayName = 'PasswordIcon';\n\nexport default Password;\n"
  },
  {
    "path": "app/assets/icons/PauseIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M10.65 19.11V4.89C10.65 3.54 10.08 3 8.64 3H5.01C3.57 3 3 3.54 3 4.89V19.11C3 20.46 3.57 21 5.01 21H8.64C10.08 21 10.65 20.46 10.65 19.11Z\"\n      fill={color}\n    />\n    <path\n      d=\"M21.0016 19.11V4.89C21.0016 3.54 20.4316 3 18.9916 3H15.3616C13.9316 3 13.3516 3.54 13.3516 4.89V19.11C13.3516 20.46 13.9216 21 15.3616 21H18.9916C20.4316 21 21.0016 20.46 21.0016 19.11Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g>\n    <path\n      d=\"M10.65 19.11V4.89C10.65 3.54 10.08 3 8.64 3H5.01C3.57 3 3 3.54 3 4.89V19.11C3 20.46 3.57 21 5.01 21H8.64C10.08 21 10.65 20.46 10.65 19.11Z\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M21 19.11V4.89C21 3.54 20.43 3 18.99 3H15.36C13.93 3 13.35 3.54 13.35 4.89V19.11C13.35 20.46 13.92 21 15.36 21H18.99C20.43 21 21 20.46 21 19.11Z\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n  </g>\n);\n\ninterface IPauseProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst Pause = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IPauseProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nPause.displayName = 'PauseIcon';\n\nexport default Pause;\n"
  },
  {
    "path": "app/assets/icons/PhotoIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(1.999 2)\">\n    <path\n      d=\"M5.638,20a6.641,6.641,0,0,1-1.082-.094L4.524,19.9a5.232,5.232,0,0,1-3.268-1.872c-.005,0-.007,0-.011-.009a.053.053,0,0,0-.007-.01A6.2,6.2,0,0,1,0,14.1V5.888C0,2.366,2.265,0,5.638,0h8.717C17.731,0,20,2.366,20,5.888V14.1a2.3,2.3,0,0,1-.019.256c-.006.058-.013.116-.016.176,0,.034,0,.068,0,.1,0,.05,0,.1-.007.153a.537.537,0,0,1-.009.055.393.393,0,0,0-.01.057,6.709,6.709,0,0,1-.155.9c-.014.059-.03.119-.045.175l-.015.054a6.059,6.059,0,0,1-.287.807c-.021.047-.043.092-.066.138-.014.026-.025.05-.037.075a5.376,5.376,0,0,1-.407.7c-.031.043-.062.084-.094.122l-.055.07a4.914,4.914,0,0,1-.513.581c-.037.035-.076.067-.115.1s-.053.042-.078.065a5.246,5.246,0,0,1-.608.461c-.047.029-.1.054-.146.078-.033.016-.066.032-.1.05a5.223,5.223,0,0,1-.687.333,1.848,1.848,0,0,1-.186.055c-.042.012-.086.023-.129.036l-.076.023a5.194,5.194,0,0,1-.657.172,3.755,3.755,0,0,1-.42.041l-.188.014c-.068,0-.135.014-.2.021a2.608,2.608,0,0,1-.32.026ZM1.4,5.888V14.1a5.328,5.328,0,0,0,.385,2.038l.358-.436c.563-.687,1.611-1.963,1.619-1.971A3.616,3.616,0,0,1,6.4,12.259a2.71,2.71,0,0,1,1.05.22,6.455,6.455,0,0,1,1.01.561l.09.058a2.329,2.329,0,0,0,1.186.537c.024,0,.049,0,.074,0a1.04,1.04,0,0,0,.41-.143,13.5,13.5,0,0,0,2.123-2.619c.057-.081.094-.134.107-.15a3.345,3.345,0,0,1,2.619-1.4,2.983,2.983,0,0,1,1.551.441c.194.116,1.535,1.052,1.981,1.431v-5.3c0-2.73-1.669-4.493-4.251-4.493H5.638C3.061,1.395,1.4,3.158,1.4,5.888ZM4.06,6.993A2.4,2.4,0,0,1,4,6.468a2.5,2.5,0,0,1,5,.046,2.495,2.495,0,0,1-4.941.479Z\"\n      fill={color}\n    />\n  </g>\n);\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g transform=\"translate(2 2)\">\n    <path\n      d=\"M13.553,0H4.9C1.889,0,0,2.134,0,5.154V13.3c0,3.02,1.881,5.154,4.9,5.154h8.648c3.024,0,4.905-2.134,4.905-5.154V5.154C18.457,2.134,16.576,0,13.553,0Z\"\n      transform=\"translate(0.75 0.75)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M3.691,1.846A1.846,1.846,0,1,1,1.845,0,1.847,1.847,0,0,1,3.691,1.846Z\"\n      transform=\"translate(5.012 4.939)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M17.457,2.929a22.809,22.809,0,0,0-3-2.593A2.345,2.345,0,0,0,11.3,1.1c-.765.991-1.243,2.324-2.4,2.949-1.423.771-2.259-.472-3.446-.97-1.325-.555-2.331.443-3.105,1.4S.788,6.389,0,7.339\"\n      transform=\"translate(1.75 10.022)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n  </g>\n);\n\ninterface IPhotoProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst Photo = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IPhotoProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nPhoto.displayName = 'PhotoIcon';\n\nexport default Photo;\n"
  },
  {
    "path": "app/assets/icons/PlayIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M17.49 9.59965L5.6 16.7696C4.9 17.1896 4 16.6896 4 15.8696V7.86965C4 4.37965 7.77 2.19965 10.8 3.93965L15.39 6.57965L17.48 7.77965C18.17 8.18965 18.18 9.18965 17.49 9.59965Z\"\n      fill={color}\n    />\n    <path\n      d=\"M18.0888 15.4606L14.0388 17.8006L9.99883 20.1306C8.54883 20.9606 6.90883 20.7906 5.71883 19.9506C5.13883 19.5506 5.20883 18.6606 5.81883 18.3006L18.5288 10.6806C19.1288 10.3206 19.9188 10.6606 20.0288 11.3506C20.2788 12.9006 19.6388 14.5706 18.0888 15.4606Z\"\n      fill={color}\n    />\n  </g>\n);\nconst Circle1Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M11.9688 2C6.44875 2 1.96875 6.48 1.96875 12C1.96875 17.52 6.44875 22 11.9688 22C17.4888 22 21.9688 17.52 21.9688 12C21.9688 6.48 17.4988 2 11.9688 2ZM14.9688 14.23L12.0687 15.9C11.7087 16.11 11.3088 16.21 10.9187 16.21C10.5188 16.21 10.1287 16.11 9.76875 15.9C9.04875 15.48 8.61875 14.74 8.61875 13.9V10.55C8.61875 9.72 9.04875 8.97 9.76875 8.55C10.4888 8.13 11.3487 8.13 12.0787 8.55L14.9787 10.22C15.6987 10.64 16.1287 11.38 16.1287 12.22C16.1287 13.06 15.6987 13.81 14.9688 14.23Z\"\n      fill={color}\n    />\n  </g>\n);\nconst Circle2Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M19.0717 19.8211C18.8817 19.8211 18.6917 19.7511 18.5417 19.6011C18.2517 19.3111 18.2517 18.8311 18.5417 18.5411C22.1517 14.9311 22.1517 9.06109 18.5417 5.46109C18.2517 5.17109 18.2517 4.69109 18.5417 4.40109C18.8317 4.11109 19.3117 4.11109 19.6017 4.40109C23.7917 8.59109 23.7917 15.4111 19.6017 19.6011C19.4517 19.7511 19.2617 19.8211 19.0717 19.8211Z\"\n      fill={color}\n    />\n    <path\n      d=\"M4.93031 19.8211C4.74031 19.8211 4.55031 19.7511 4.40031 19.6011C0.210312 15.4111 0.210312 8.59109 4.40031 4.40109C4.69031 4.11109 5.17031 4.11109 5.46031 4.40109C5.75031 4.69109 5.75031 5.17109 5.46031 5.46109C1.85031 9.07109 1.85031 14.9411 5.46031 18.5411C5.75031 18.8311 5.75031 19.3111 5.46031 19.6011C5.31031 19.7511 5.12031 19.8211 4.93031 19.8211Z\"\n      fill={color}\n    />\n    <path\n      d=\"M11.9988 22.7119C10.7488 22.7019 9.55878 22.5019 8.44878 22.1119C8.05878 21.9719 7.84878 21.5419 7.98878 21.1519C8.12878 20.7619 8.54878 20.5519 8.94878 20.6919C9.90878 21.0219 10.9288 21.2019 12.0088 21.2019C13.0788 21.2019 14.1088 21.0219 15.0588 20.6919C15.4488 20.5619 15.8788 20.7619 16.0188 21.1519C16.1588 21.5419 15.9488 21.9719 15.5588 22.1119C14.4388 22.5019 13.2488 22.7119 11.9988 22.7119Z\"\n      fill={color}\n    />\n    <path\n      d=\"M15.2988 3.33906C15.2188 3.33906 15.1288 3.32906 15.0488 3.29906C14.0988 2.95906 13.0688 2.78906 11.9988 2.78906C10.9288 2.78906 9.90878 2.96906 8.94878 3.29906C8.54878 3.42906 8.12878 3.22906 7.98878 2.83906C7.84878 2.44906 8.05878 2.01906 8.44878 1.87906C9.56878 1.48906 10.7588 1.28906 11.9988 1.28906C13.2388 1.28906 14.4388 1.48906 15.5488 1.87906C15.9388 2.01906 16.1488 2.44906 16.0088 2.83906C15.8988 3.14906 15.6088 3.33906 15.2988 3.33906Z\"\n      fill={color}\n    />\n    <path\n      d=\"M8.73828 12.0001V10.3301C8.73828 8.25014 10.2083 7.40014 12.0083 8.44014L13.4583 9.28014L14.9083 10.1201C16.7083 11.1601 16.7083 12.8601 14.9083 13.9001L13.4583 14.7401L12.0083 15.5801C10.2083 16.6201 8.73828 15.7701 8.73828 13.6901V12.0001Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g>\n    <path\n      d=\"M4 11.9999V8.43989C4 4.01989 7.13 2.2099 10.96 4.4199L14.05 6.1999L17.14 7.9799C20.97 10.1899 20.97 13.8099 17.14 16.0199L14.05 17.7999L10.96 19.5799C7.13 21.7899 4 19.9799 4 15.5599V11.9999Z\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeMiterlimit=\"10\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n  </g>\n);\n\nconst Circle1Light = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M11.9688 22.75C6.04875 22.75 1.21875 17.93 1.21875 12C1.21875 6.07 6.04875 1.25 11.9688 1.25C17.8888 1.25 22.7188 6.07 22.7188 12C22.7188 17.93 17.8988 22.75 11.9688 22.75ZM11.9688 2.75C6.86875 2.75 2.71875 6.9 2.71875 12C2.71875 17.1 6.86875 21.25 11.9688 21.25C17.0688 21.25 21.2188 17.1 21.2188 12C21.2188 6.9 17.0688 2.75 11.9688 2.75Z\"\n      fill={color}\n    />\n    <path\n      d=\"M10.5583 16.9915C10.1183 16.9915 9.69828 16.8815 9.32828 16.6715C8.46828 16.1715 7.98828 15.1915 7.98828 13.9115V10.5615C7.98828 9.28146 8.45828 8.30146 9.31828 7.80146C10.1783 7.30146 11.2683 7.38146 12.3783 8.02146L15.2783 9.69146C16.3883 10.3315 16.9983 11.2315 16.9983 12.2315C16.9983 13.2215 16.3883 14.1315 15.2783 14.7715L12.3783 16.4415C11.7583 16.8115 11.1283 16.9915 10.5583 16.9915ZM10.5583 8.97146C10.3783 8.97146 10.2083 9.01146 10.0783 9.09146C9.69828 9.31146 9.48828 9.84146 9.48828 10.5615V13.9115C9.48828 14.6215 9.69828 15.1615 10.0783 15.3715C10.4483 15.5915 11.0183 15.5015 11.6383 15.1515L14.5383 13.4815C15.1583 13.1215 15.5083 12.6715 15.5083 12.2415C15.5083 11.8115 15.1483 11.3615 14.5383 11.0015L11.6383 9.33146C11.2383 9.09146 10.8683 8.97146 10.5583 8.97146Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Circle2Light = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M19.0678 19.8211C18.8778 19.8211 18.6878 19.751 18.5378 19.601C18.2478 19.311 18.2478 18.831 18.5378 18.541C22.1478 14.931 22.1478 9.06109 18.5378 5.46109C18.2478 5.17109 18.2478 4.69109 18.5378 4.40109C18.8278 4.11109 19.3078 4.11109 19.5978 4.40109C23.7878 8.59109 23.7878 15.411 19.5978 19.601C19.4478 19.751 19.2578 19.8211 19.0678 19.8211Z\"\n      fill={color}\n    />\n    <path\n      d=\"M4.93031 19.8211C4.74031 19.8211 4.55031 19.751 4.40031 19.601C0.210312 15.411 0.210312 8.59109 4.40031 4.40109C4.69031 4.11109 5.17031 4.11109 5.46031 4.40109C5.75031 4.69109 5.75031 5.17109 5.46031 5.46109C1.85031 9.07109 1.85031 14.941 5.46031 18.541C5.75031 18.831 5.75031 19.311 5.46031 19.601C5.31031 19.751 5.12031 19.8211 4.93031 19.8211Z\"\n      fill={color}\n    />\n    <path\n      d=\"M11.9988 22.7119C10.7488 22.7019 9.5588 22.5019 8.4488 22.1119C8.0588 21.9719 7.84878 21.5419 7.98878 21.1519C8.12878 20.7619 8.5488 20.5519 8.9488 20.6919C9.9088 21.0219 10.9288 21.2019 12.0088 21.2019C13.0788 21.2019 14.1088 21.0219 15.0588 20.6919C15.4488 20.5619 15.8788 20.7619 16.0188 21.1519C16.1588 21.5419 15.9488 21.9719 15.5588 22.1119C14.4388 22.5019 13.2488 22.7119 11.9988 22.7119Z\"\n      fill={color}\n    />\n    <path\n      d=\"M15.3009 3.33911C15.2209 3.33911 15.1309 3.32907 15.0509 3.29907C14.0909 2.96907 13.061 2.78906 11.991 2.78906C10.9209 2.78906 9.90096 2.96907 8.94096 3.29907C8.55096 3.42907 8.12097 3.22911 7.98097 2.83911C7.84097 2.44911 8.05096 2.01909 8.44096 1.87909C9.55096 1.48909 10.751 1.28906 11.991 1.28906C13.231 1.28906 14.431 1.48909 15.541 1.87909C15.931 2.01909 16.141 2.44911 16.001 2.83911C15.901 3.14911 15.6109 3.33911 15.3009 3.33911Z\"\n      fill={color}\n    />\n    <path\n      d=\"M10.5622 16.761C10.1222 16.761 9.70218 16.651 9.33218 16.441C8.47218 15.941 7.99219 14.961 7.99219 13.681V10.331C7.99219 9.05101 8.46218 8.071 9.33218 7.571C10.1922 7.071 11.2822 7.15097 12.3922 7.79097L15.2922 9.46102C16.4022 10.101 17.0122 11.001 17.0122 12.001C17.0122 13.001 16.4022 13.901 15.2922 14.541L12.3922 16.211C11.7622 16.581 11.1322 16.761 10.5622 16.761ZM10.5622 8.74098C10.3822 8.74098 10.2122 8.78098 10.0822 8.86098C9.71218 9.08098 9.49219 9.611 9.49219 10.321V13.671C9.49219 14.381 9.70218 14.921 10.0822 15.131C10.4522 15.341 11.0222 15.261 11.6422 14.911L14.5422 13.241C15.1622 12.881 15.5122 12.431 15.5122 12.001C15.5122 11.571 15.1622 11.121 14.5422 10.761L11.6422 9.09102C11.2422 8.86102 10.8722 8.74098 10.5622 8.74098Z\"\n      fill={color}\n    />\n  </g>\n);\n\ninterface IPlayProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The type of the icon\n   * @default 'normal'\n   * @type 'normal' | 'circle1' | 'circle2'\n   */\n  type?: 'normal' | 'circle1' | 'circle2';\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst Play = ({\n  fill = 'currentColor',\n  filled = false,\n  type = 'normal',\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IPlayProps) => {\n  switch (type) {\n    case 'normal':\n      if (filled) {\n        return (\n          <svg\n            className=\"\"\n            width={width || size}\n            height={height || size}\n            viewBox=\"0 0 24 24\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n            style={{\n              display: 'inline',\n            }}\n            {...props}\n          >\n            <Bold color={fill} />\n          </svg>\n        );\n      }\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    case 'circle1':\n      if (filled) {\n        return (\n          <svg\n            className=\"\"\n            width={width || size}\n            height={height || size}\n            viewBox=\"0 0 24 24\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n            style={{\n              display: 'inline',\n            }}\n            {...props}\n          >\n            <Circle1Bold color={fill} />\n          </svg>\n        );\n      }\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Circle1Light color={fill} />\n        </svg>\n      );\n    case 'circle2':\n      if (filled) {\n        return (\n          <svg\n            className=\"\"\n            width={width || size}\n            height={height || size}\n            viewBox=\"0 0 24 24\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n            style={{\n              display: 'inline',\n            }}\n            {...props}\n          >\n            <Circle2Bold color={fill} />\n          </svg>\n        );\n      }\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Circle2Light color={fill} />\n        </svg>\n      );\n    default:\n      if (filled) {\n        return (\n          <svg\n            className=\"\"\n            width={width || size}\n            height={height || size}\n            viewBox=\"0 0 24 24\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n            style={{\n              display: 'inline',\n            }}\n            {...props}\n          >\n            <Bold color={fill} />\n          </svg>\n        );\n      }\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n  }\n};\n\nPlay.displayName = 'PlayIcon';\n\nexport default Play;\n"
  },
  {
    "path": "app/assets/icons/PreviousIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M20.2409 7.21957V16.7896C20.2409 18.7496 18.1109 19.9796 16.4109 18.9996L12.2609 16.6096L8.11094 14.2096C6.41094 13.2296 6.41094 10.7796 8.11094 9.79957L12.2609 7.39957L16.4109 5.00957C18.1109 4.02957 20.2409 5.24957 20.2409 7.21957Z\"\n      fill={color}\n    />\n    <path\n      d=\"M3.76172 18.9303C3.35172 18.9303 3.01172 18.5903 3.01172 18.1803V5.82031C3.01172 5.41031 3.35172 5.07031 3.76172 5.07031C4.17172 5.07031 4.51172 5.41031 4.51172 5.82031V18.1803C4.51172 18.5903 4.17172 18.9303 3.76172 18.9303Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M17.6916 20.0902C17.1216 20.0902 16.5616 19.9402 16.0416 19.6402L7.75156 14.8602C6.72156 14.2602 6.10156 13.1902 6.10156 12.0002C6.10156 10.8102 6.72156 9.74019 7.75156 9.14019L16.0416 4.36016C17.0716 3.76016 18.3016 3.76016 19.3416 4.36016C20.3816 4.96016 20.9915 6.02017 20.9915 7.22017V16.7902C20.9915 17.9802 20.3716 19.0502 19.3416 19.6502C18.8216 19.9402 18.2616 20.0902 17.6916 20.0902ZM17.6916 5.41017C17.3816 5.41017 17.0716 5.49016 16.7916 5.65016L8.50156 10.4302C7.94156 10.7602 7.60156 11.3402 7.60156 11.9902C7.60156 12.6402 7.94156 13.2202 8.50156 13.5502L16.7916 18.3302C17.3516 18.6602 18.0316 18.6602 18.5916 18.3302C19.1516 18.0002 19.4915 17.4202 19.4915 16.7702V7.20018C19.4915 6.55018 19.1516 5.97019 18.5916 5.64019C18.3116 5.50019 18.0016 5.41017 17.6916 5.41017Z\"\n      fill={color}\n    />\n    <path\n      d=\"M3.75781 18.9303C3.34781 18.9303 3.00781 18.5903 3.00781 18.1803V5.82031C3.00781 5.41031 3.34781 5.07031 3.75781 5.07031C4.16781 5.07031 4.50781 5.41031 4.50781 5.82031V18.1803C4.50781 18.5903 4.16781 18.9303 3.75781 18.9303Z\"\n      fill={color}\n    />\n  </g>\n);\n\ninterface IPreviousProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst Previous = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IPreviousProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nPrevious.displayName = 'PreviousIcon';\n\nexport default Previous;\n"
  },
  {
    "path": "app/assets/icons/RatioIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <rect\n      x=\"6\"\n      y=\"6\"\n      width=\"36\"\n      height=\"36\"\n      rx=\"3\"\n      fill={color}\n      stroke={color}\n      strokeWidth=\"2\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      fillRule=\"evenodd\"\n      clipRule=\"evenodd\"\n      d=\"M24 22.5C25.3807 22.5 26.5 21.3807 26.5 20C26.5 18.6193 25.3807 17.5 24 17.5C22.6193 17.5 21.5 18.6193 21.5 20C21.5 21.3807 22.6193 22.5 24 22.5Z\"\n      fill=\"#FFF\"\n    />\n    <path\n      fillRule=\"evenodd\"\n      clipRule=\"evenodd\"\n      d=\"M24 30.5C25.3807 30.5 26.5 29.3807 26.5 28C26.5 26.6193 25.3807 25.5 24 25.5C22.6193 25.5 21.5 26.6193 21.5 28C21.5 29.3807 22.6193 30.5 24 30.5Z\"\n      fill=\"#FFF\"\n    />\n    <path\n      d=\"M15.5 17V31\"\n      stroke=\"#FFF\"\n      strokeWidth=\"2\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M32.5 17V31\"\n      stroke=\"#FFF\"\n      strokeWidth=\"2\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g>\n    <rect\n      x=\"6\"\n      y=\"6\"\n      width=\"36\"\n      height=\"36\"\n      rx=\"3\"\n      fill=\"none\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <path\n      fillRule=\"evenodd\"\n      clipRule=\"evenodd\"\n      d=\"M24 22.5C25.3807 22.5 26.5 21.3807 26.5 20C26.5 18.6193 25.3807 17.5 24 17.5C22.6193 17.5 21.5 18.6193 21.5 20C21.5 21.3807 22.6193 22.5 24 22.5Z\"\n      fill={color}\n    />\n    <path\n      fillRule=\"evenodd\"\n      clipRule=\"evenodd\"\n      d=\"M24 30.5C25.3807 30.5 26.5 29.3807 26.5 28C26.5 26.6193 25.3807 25.5 24 25.5C22.6193 25.5 21.5 26.6193 21.5 28C21.5 29.3807 22.6193 30.5 24 30.5Z\"\n      fill={color}\n    />\n    <path\n      d=\"M15.5 17V31\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M32.5 17V31\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n  </g>\n);\n\ninterface IRatioProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst Ratio = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IRatioProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 48 48\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={2} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 48 48\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nRatio.displayName = 'RatioIcon';\n\nexport default Ratio;\n"
  },
  {
    "path": "app/assets/icons/RefreshIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M14.55 22.42C14.22 22.42 13.91 22.2 13.82 21.86C13.72 21.46 13.95 21.05 14.35 20.94C18.41 19.87 21.24 16.19 21.24 11.99C21.24 6.89 17.09 2.74 11.99 2.74C7.66 2.74 4.82 5.27 3.49 6.8H6.43C6.84 6.8 7.18 7.14 7.18 7.55C7.18 7.96 6.86 8.31 6.44 8.31H2.01C1.94 8.31 1.87 8.3 1.8 8.28C1.71 8.25 1.63 8.21 1.56 8.16C1.47 8.1 1.4 8.02 1.35 7.93C1.3 7.83 1.26 7.73 1.25 7.62C1.25 7.59 1.25 7.57 1.25 7.54V3C1.25 2.59 1.59 2.25 2 2.25C2.41 2.25 2.75 2.59 2.75 3V5.39C4.38 3.64 7.45 1.25 12 1.25C17.93 1.25 22.75 6.07 22.75 12C22.75 16.88 19.46 21.16 14.74 22.4C14.68 22.41 14.61 22.42 14.55 22.42Z\"\n      fill={color}\n    />\n    <path\n      d=\"M11.29 22.73C11.27 22.73 11.24 22.73 11.22 22.72C10.15 22.65 9.1 22.41 8.1 22.02C7.81 21.91 7.62 21.62 7.62 21.31C7.62 21.22 7.64 21.13 7.67 21.05C7.82 20.67 8.26 20.47 8.64 20.62C9.5 20.96 10.41 21.16 11.33 21.23H11.34C11.74 21.25 12.04 21.59 12.04 21.99C12.04 22 12.04 22.02 12.04 22.03C12.01 22.42 11.68 22.73 11.29 22.73ZM5.78 20.58C5.61 20.58 5.45 20.53 5.31 20.42C4.47 19.75 3.74 18.96 3.13 18.07C3.04 17.94 2.99 17.8 2.99 17.65C2.99 17.4 3.11 17.17 3.32 17.03C3.65 16.8 4.14 16.89 4.36 17.22C4.89 17.99 5.52 18.67 6.25 19.24C6.42 19.38 6.53 19.59 6.53 19.82C6.53 19.99 6.48 20.16 6.37 20.3C6.23 20.48 6.01 20.58 5.78 20.58ZM2.44 15.7C2.11 15.7 1.82 15.49 1.73 15.18C1.41 14.15 1.25 13.08 1.25 12C1.25 11.59 1.59 11.25 2 11.25C2.41 11.25 2.75 11.59 2.75 12C2.75 12.93 2.89 13.85 3.16 14.73C3.18 14.8 3.19 14.88 3.19 14.96C3.19 15.29 2.98 15.57 2.67 15.67C2.59 15.69 2.52 15.7 2.44 15.7Z\"\n      fill={color}\n    />\n    <path\n      d=\"M12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 14.2091 9.79086 16 12 16Z\"\n      fill={color}\n    />\n  </g>\n);\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g>\n    <path\n      d=\"M14.55 21.67C18.84 20.54 22 16.64 22 12C22 6.48 17.56 2 12 2C5.33 2 2 7.56 2 7.56M2 7.56V3M2 7.56H4.01H6.44\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M2 12C2 17.52 6.48 22 12 22\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeDasharray=\"3 3\"\n    />\n  </g>\n);\n\ninterface IRefreshProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst RefreshIcon = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IRefreshProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nRefreshIcon.displayName = 'RefreshIcon';\n\nexport default RefreshIcon;\n"
  },
  {
    "path": "app/assets/icons/SearchIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(2 2)\">\n    <path\n      d=\"M17.741,19.608l-2.12-2.43a1.083,1.083,0,0,1,0-1.524.986.986,0,0,1,1.393,0l2.554,2.062h.045a1.348,1.348,0,0,1,0,1.892,1.315,1.315,0,0,1-1.872,0ZM0,8.67A8.624,8.624,0,0,1,8.578,0a8.531,8.531,0,0,1,6.065,2.54,8.716,8.716,0,0,1,2.512,6.13A8.624,8.624,0,0,1,8.578,17.34,8.624,8.624,0,0,1,0,8.67Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g\n    transform=\"translate(2 2)\"\n    fill=\"none\"\n    stroke={color}\n    strokeLinecap=\"round\"\n    strokeLinejoin=\"round\"\n    strokeMiterlimit={10}\n    strokeWidth={strokeWidth}\n  >\n    <circle cx={8.989} cy={8.989} r={8.989} transform=\"translate(.778 .778)\" />\n    <path d=\"M16.018 16.485L19.542 20\" />\n  </g>\n);\n\ninterface ISearchProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst Search = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: ISearchProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nSearch.displayName = 'SearchIcon';\n\nexport default Search;\n"
  },
  {
    "path": "app/assets/icons/SettingsIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(2.5 2)\">\n    <path\n      d=\"M10.2,20H8.807a2.066,2.066,0,0,1-2.125-2.05A1.9,1.9,0,0,0,4.8,16.13a1.58,1.58,0,0,0-.9.23,2.163,2.163,0,0,1-1.084.3A2.122,2.122,0,0,1,1,15.62l-.7-1.2a2,2,0,0,1-.021-2.05,2.108,2.108,0,0,1,.817-.789,1.653,1.653,0,0,0,.644-.64,1.782,1.782,0,0,0,.19-1.365A1.837,1.837,0,0,0,1.071,8.44,2.045,2.045,0,0,1,.314,5.61L1,4.43a2.123,2.123,0,0,1,2.882-.76,1.894,1.894,0,0,0,.9.224A1.959,1.959,0,0,0,6.446,2.98a1.538,1.538,0,0,0,.236-.88A1.788,1.788,0,0,1,6.968,1.04,2.2,2.2,0,0,1,8.776,0h1.441a2.154,2.154,0,0,1,1.82,1.04A1.781,1.781,0,0,1,12.312,2.1a1.545,1.545,0,0,0,.235.88,1.964,1.964,0,0,0,1.672.914,1.926,1.926,0,0,0,.9-.224,2.111,2.111,0,0,1,2.872.76l.684,1.18a2.027,2.027,0,0,1-.756,2.831,1.829,1.829,0,0,0-.853,1.138,1.771,1.771,0,0,0,.2,1.362,1.571,1.571,0,0,0,.634.64,2.307,2.307,0,0,1,.828.789,2.031,2.031,0,0,1-.02,2.05l-.715,1.2a2.1,2.1,0,0,1-2.893.74,1.621,1.621,0,0,0-.9-.23,1.9,1.9,0,0,0-1.891,1.82A2.061,2.061,0,0,1,10.2,20ZM9.512,7.18a2.87,2.87,0,0,0-2.9,2.83,2.763,2.763,0,0,0,.849,2,2.93,2.93,0,0,0,2.053.821A2.822,2.822,0,0,0,11.55,8.006,2.877,2.877,0,0,0,9.512,7.18Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g transform=\"translate(2.5 1.5)\">\n    <path\n      d=\"M17.528,5.346l-.622-1.08a1.913,1.913,0,0,0-2.609-.7h0a1.9,1.9,0,0,1-2.609-.677,1.831,1.831,0,0,1-.256-.915h0A1.913,1.913,0,0,0,9.519,0H8.265a1.9,1.9,0,0,0-1.9,1.913h0A1.913,1.913,0,0,1,4.448,3.8a1.831,1.831,0,0,1-.915-.256h0a1.913,1.913,0,0,0-2.609.7l-.668,1.1a1.913,1.913,0,0,0,.7,2.609h0a1.913,1.913,0,0,1,.957,1.657,1.913,1.913,0,0,1-.957,1.657h0a1.9,1.9,0,0,0-.7,2.6h0l.632,1.089A1.913,1.913,0,0,0,3.5,15.7h0a1.895,1.895,0,0,1,2.6.7,1.831,1.831,0,0,1,.256.915h0a1.913,1.913,0,0,0,1.913,1.913H9.519a1.913,1.913,0,0,0,1.913-1.9h0a1.9,1.9,0,0,1,1.913-1.913,1.95,1.95,0,0,1,.915.256h0a1.913,1.913,0,0,0,2.609-.7h0l.659-1.1a1.9,1.9,0,0,0-.7-2.609h0a1.9,1.9,0,0,1-.7-2.609,1.876,1.876,0,0,1,.7-.7h0a1.913,1.913,0,0,0,.7-2.6h0Z\"\n      transform=\"translate(0.779 0.778)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <circle\n      cx=\"2.636\"\n      cy=\"2.636\"\n      r=\"2.636\"\n      transform=\"translate(7.039 7.753)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n  </g>\n);\n\ninterface ISettingsProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst Settings = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: ISettingsProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nSettings.displayName = 'SettingsIcon';\n\nexport default Settings;\n"
  },
  {
    "path": "app/assets/icons/ShareIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M20.3591 12.7315C19.9891 12.7315 19.6791 12.4515 19.6391 12.0815C19.3991 9.88154 18.2191 7.90154 16.3991 6.64154C16.0691 6.41154 15.9891 5.96154 16.2191 5.63154C16.4491 5.30154 16.8991 5.22154 17.2291 5.45154C19.3991 6.96154 20.7991 9.32154 21.0891 11.9315C21.1291 12.3315 20.8391 12.6915 20.4391 12.7315C20.4091 12.7315 20.3891 12.7315 20.3591 12.7315Z\"\n      fill={color}\n    />\n    <path\n      d=\"M3.73931 12.7812C3.71931 12.7812 3.68931 12.7812 3.66931 12.7812C3.26931 12.7412 2.97931 12.3812 3.01931 11.9812C3.28931 9.37118 4.66931 7.01118 6.81931 5.49118C7.13931 5.26118 7.59931 5.34118 7.82931 5.66118C8.05931 5.99118 7.97931 6.44118 7.65931 6.67118C5.85931 7.95118 4.68931 9.93118 4.46931 12.1212C4.42931 12.5012 4.10931 12.7812 3.73931 12.7812Z\"\n      fill={color}\n    />\n    <path\n      d=\"M15.9906 21.1003C14.7606 21.6903 13.4406 21.9903 12.0606 21.9903C10.6206 21.9903 9.25059 21.6703 7.97059 21.0203C7.61059 20.8503 7.47059 20.4103 7.65059 20.0503C7.82059 19.6903 8.26059 19.5503 8.62059 19.7203C9.25059 20.0403 9.92059 20.2603 10.6006 20.3903C11.5206 20.5703 12.4606 20.5803 13.3806 20.4203C14.0606 20.3003 14.7306 20.0903 15.3506 19.7903C15.7206 19.6203 16.1606 19.7603 16.3206 20.1303C16.5006 20.4903 16.3606 20.9303 15.9906 21.1003Z\"\n      fill={color}\n    />\n    <path\n      d=\"M12.0505 2.01172C10.5005 2.01172 9.23047 3.27172 9.23047 4.83172C9.23047 6.39172 10.4905 7.65172 12.0505 7.65172C13.6105 7.65172 14.8705 6.39172 14.8705 4.83172C14.8705 3.27172 13.6105 2.01172 12.0505 2.01172Z\"\n      fill={color}\n    />\n    <path\n      d=\"M5.05047 13.8711C3.50047 13.8711 2.23047 15.1311 2.23047 16.6911C2.23047 18.2511 3.49047 19.5111 5.05047 19.5111C6.61047 19.5111 7.87047 18.2511 7.87047 16.6911C7.87047 15.1311 6.60047 13.8711 5.05047 13.8711Z\"\n      fill={color}\n    />\n    <path\n      d=\"M18.9509 13.8711C17.4009 13.8711 16.1309 15.1311 16.1309 16.6911C16.1309 18.2511 17.3909 19.5111 18.9509 19.5111C20.5109 19.5111 21.7709 18.2511 21.7709 16.6911C21.7709 15.1311 20.5109 13.8711 18.9509 13.8711Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M20.6196 13.0701C20.2396 13.0701 19.9196 12.7801 19.8696 12.4001C19.6296 10.1401 18.4096 8.0901 16.5296 6.7901C16.1896 6.5501 16.1096 6.0901 16.3396 5.7501C16.5796 5.4101 17.0496 5.3301 17.3796 5.5601C19.6196 7.1101 21.0596 9.5501 21.3496 12.2401C21.3896 12.6501 21.0996 13.0201 20.6796 13.0701C20.6696 13.0701 20.6396 13.0701 20.6196 13.0701Z\"\n      fill={color}\n    />\n    <path\n      d=\"M3.48991 13.12C3.45991 13.12 3.43991 13.12 3.40991 13.12C2.99991 13.07 2.69991 12.7 2.73991 12.29C3.00991 9.60001 4.43991 7.17001 6.64991 5.60001C6.98991 5.36001 7.45991 5.44001 7.69991 5.78001C7.93991 6.12001 7.85991 6.59001 7.51991 6.83001C5.65991 8.14001 4.45991 10.19 4.22991 12.45C4.19991 12.83 3.86991 13.12 3.48991 13.12Z\"\n      fill={color}\n    />\n    <path\n      d=\"M12.06 22.61C10.58 22.61 9.16997 22.27 7.84997 21.61C7.47997 21.42 7.32997 20.97 7.51997 20.6C7.70997 20.23 8.15997 20.08 8.52997 20.27C10.69 21.36 13.29 21.38 15.47 20.33C15.84 20.15 16.29 20.31 16.47 20.68C16.65 21.05 16.49 21.5 16.12 21.68C14.84 22.3 13.48 22.61 12.06 22.61Z\"\n      fill={color}\n    />\n    <path\n      d=\"M12.0593 8.43988C10.1093 8.43988 8.5293 6.85988 8.5293 4.90988C8.5293 2.95988 10.1093 1.37988 12.0593 1.37988C14.0093 1.37988 15.5893 2.95988 15.5893 4.90988C15.5893 6.85988 13.9993 8.43988 12.0593 8.43988ZM12.0593 2.88988C10.9393 2.88988 10.0293 3.79988 10.0293 4.91988C10.0293 6.03988 10.9393 6.94988 12.0593 6.94988C13.1793 6.94988 14.0893 6.03988 14.0893 4.91988C14.0893 3.79988 13.1693 2.88988 12.0593 2.88988Z\"\n      fill={color}\n    />\n    <path\n      d=\"M4.83078 20.6701C2.88078 20.6701 1.30078 19.0901 1.30078 17.1401C1.30078 15.2001 2.88078 13.6101 4.83078 13.6101C6.78078 13.6101 8.36078 15.1901 8.36078 17.1401C8.36078 19.0801 6.78078 20.6701 4.83078 20.6701ZM4.83078 15.1101C3.71078 15.1101 2.80078 16.0201 2.80078 17.1401C2.80078 18.2601 3.71078 19.1701 4.83078 19.1701C5.95078 19.1701 6.86078 18.2601 6.86078 17.1401C6.86078 16.0201 5.95078 15.1101 4.83078 15.1101Z\"\n      fill={color}\n    />\n    <path\n      d=\"M19.1706 20.6701C17.2206 20.6701 15.6406 19.0901 15.6406 17.1401C15.6406 15.2001 17.2206 13.6101 19.1706 13.6101C21.1206 13.6101 22.7006 15.1901 22.7006 17.1401C22.6906 19.0801 21.1106 20.6701 19.1706 20.6701ZM19.1706 15.1101C18.0506 15.1101 17.1406 16.0201 17.1406 17.1401C17.1406 18.2601 18.0506 19.1701 19.1706 19.1701C20.2906 19.1701 21.2006 18.2601 21.2006 17.1401C21.1906 16.0201 20.2906 15.1101 19.1706 15.1101Z\"\n      fill={color}\n    />\n  </g>\n);\n\ninterface IShareProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst Share = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IShareProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nShare.displayName = 'ShareIcon';\n\nexport default Share;\n"
  },
  {
    "path": "app/assets/icons/SortIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(2 3)\">\n    <path\n      d=\"M14.454,17.721a4.445,4.445,0,0,1-.434-.394A17.665,17.665,0,0,1,10.909,12.7a4.061,4.061,0,0,1-.32-1.078V11.57a1.461,1.461,0,0,1,.779-1.288,6.515,6.515,0,0,1,1.04-.28A20.9,20.9,0,0,1,15.3,9.83a17.926,17.926,0,0,1,2.985.2,7.45,7.45,0,0,1,.835.209,1.5,1.5,0,0,1,.709.65,1.633,1.633,0,0,1,.171.732,3.875,3.875,0,0,1-.264.963,17.773,17.773,0,0,1-3.155,4.736c-.16.162-.355.337-.4.382a1.43,1.43,0,0,1-.891.3A1.363,1.363,0,0,1,14.454,17.721Zm-10.94-2.3.257-4.578a.934.934,0,1,1,1.867,0L5.9,15.417a1.191,1.191,0,1,1-2.381,0Zm-1.8-7.444A7.4,7.4,0,0,1,.88,7.764a1.491,1.491,0,0,1-.709-.65A1.62,1.62,0,0,1,0,6.384a3.943,3.943,0,0,1,.262-.963A17.762,17.762,0,0,1,3.418.685c.161-.162.355-.337.4-.383A1.433,1.433,0,0,1,4.712,0a1.368,1.368,0,0,1,.834.278,4.586,4.586,0,0,1,.435.4A17.751,17.751,0,0,1,9.091,5.3a4.108,4.108,0,0,1,.321,1.079v.047a1.464,1.464,0,0,1-.778,1.287A6.442,6.442,0,0,1,7.593,8,20.9,20.9,0,0,1,4.7,8.17,17.926,17.926,0,0,1,1.715,7.973Zm12.647-.811L14.1,2.583a1.191,1.191,0,1,1,2.381,0l-.257,4.579a.934.934,0,1,1-1.867,0Z\"\n      transform=\"translate(0 0)\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g transform=\"translate(2 3)\">\n    <path\n      d=\"M.556,13.618V0\"\n      transform=\"translate(14.284 3.546)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M8.156,0,4.078,4.1,0,0\"\n      transform=\"translate(10.762 13.068)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M.556,0V13.618\"\n      transform=\"translate(4.356 0.833)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M0,4.1,4.078,0,8.156,4.1\"\n      transform=\"translate(0.833 0.832)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n  </g>\n);\n\ninterface ISortProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst Sort = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: ISortProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nSort.displayName = 'SortIcon';\n\nexport default Sort;\n"
  },
  {
    "path": "app/assets/icons/StarIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M15.919,11.82a1.1,1.1,0,0,0-.319.97l.889,4.92a1.08,1.08,0,0,1-.45,1.08,1.1,1.1,0,0,1-1.17.08L10.44,16.56a1.131,1.131,0,0,0-.5-.131H9.669a.812.812,0,0,0-.27.09L4.969,18.84a1.168,1.168,0,0,1-.71.11,1.112,1.112,0,0,1-.89-1.271l.89-4.92a1.119,1.119,0,0,0-.319-.979L.329,8.28A1.08,1.08,0,0,1,.06,7.15,1.123,1.123,0,0,1,.949,6.4l4.97-.721A1.112,1.112,0,0,0,6.8,5.07L8.989.58a1.041,1.041,0,0,1,.2-.27l.09-.07A.671.671,0,0,1,9.44.11L9.549.07,9.719,0h.421a1.119,1.119,0,0,1,.88.6l2.219,4.47a1.111,1.111,0,0,0,.83.609l4.97.721a1.134,1.134,0,0,1,.91.75,1.086,1.086,0,0,1-.29,1.13Z\"\n      transform=\"translate(0 0)\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g>\n    <path\n      d=\"M10.214.441,12.53,5.1a.8.8,0,0,0,.6.437l5.185.749a.8.8,0,0,1,.528.306.77.77,0,0,1-.085,1.032L15,11.258a.762.762,0,0,0-.226.7l.9,5.128a.787.787,0,0,1-.652.892.868.868,0,0,1-.516-.08L9.888,15.478a.776.776,0,0,0-.742,0L4.494,17.912a.812.812,0,0,1-1.077-.33.8.8,0,0,1-.081-.5l.9-5.128a.788.788,0,0,0-.226-.7L.232,7.621a.786.786,0,0,1,0-1.112l0,0a.909.909,0,0,1,.452-.222L5.87,5.534a.812.812,0,0,0,.6-.438L8.784.441a.787.787,0,0,1,.458-.4.8.8,0,0,1,.61.044A.82.82,0,0,1,10.214.441Z\"\n      transform=\"translate(0 0)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n  </g>\n);\n\ninterface IStarProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst Star = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IStarProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nStar.displayName = 'StarIcon';\n\nexport default Star;\n"
  },
  {
    "path": "app/assets/icons/StopIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <path\n    d=\"M5 3C3.89543 3 3 3.89543 3 5V19C3 20.1046 3.89543 21 5 21H19C20.1046 21 21 20.1046 21 19V5C21 3.89543 20.1046 3 19 3H5Z\"\n    fill={color}\n  />\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <path\n    fillRule=\"evenodd\"\n    clipRule=\"evenodd\"\n    d=\"M3 5C3 3.89543 3.89543 3 5 3H19C20.1046 3 21 3.89543 21 5V19C21 20.1046 20.1046 21 19 21H5C3.89543 21 3 20.1046 3 19V5ZM4.5 5C4.5 4.72386 4.72386 4.5 5 4.5H19C19.2761 4.5 19.5 4.72386 19.5 5V19C19.5 19.2761 19.2761 19.5 19 19.5H5C4.72386 19.5 4.5 19.2761 4.5 19V5Z\"\n    fill={color}\n  />\n);\n\ninterface IStopProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst Stop = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IStopProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nStop.displayName = 'StopIcon';\n\nexport default Stop;\n"
  },
  {
    "path": "app/assets/icons/SubtitleIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M16.19 2H7.81C4.17 2 2 4.17 2 7.81V16.18C2 19.83 4.17 22 7.81 22H16.18C19.82 22 21.99 19.83 21.99 16.19V7.81C22 4.17 19.83 2 16.19 2ZM6.5 12.57H9.27C9.68 12.57 10.02 12.91 10.02 13.32C10.02 13.73 9.68 14.07 9.27 14.07H6.5C6.09 14.07 5.75 13.73 5.75 13.32C5.75 12.91 6.09 12.57 6.5 12.57ZM12.97 17.83H6.5C6.09 17.83 5.75 17.49 5.75 17.08C5.75 16.67 6.09 16.33 6.5 16.33H12.97C13.38 16.33 13.72 16.67 13.72 17.08C13.72 17.49 13.39 17.83 12.97 17.83ZM17.5 17.83H15.65C15.24 17.83 14.9 17.49 14.9 17.08C14.9 16.67 15.24 16.33 15.65 16.33H17.5C17.91 16.33 18.25 16.67 18.25 17.08C18.25 17.49 17.91 17.83 17.5 17.83ZM17.5 14.07H11.97C11.56 14.07 11.22 13.73 11.22 13.32C11.22 12.91 11.56 12.57 11.97 12.57H17.5C17.91 12.57 18.25 12.91 18.25 13.32C18.25 13.73 17.91 14.07 17.5 14.07Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g>\n    <path\n      d=\"M9 22H15C20 22 22 20 22 15V9C22 4 20 2 15 2H9C4 2 2 4 2 9V15C2 20 4 22 9 22Z\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M17.5 17.0801H15.65\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M12.97 17.0801H6.5\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M17.5 13.3201H11.97\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M9.27 13.3201H6.5\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n  </g>\n);\n\ninterface ISubtitleProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst Subtitle = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: ISubtitleProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nSubtitle.displayName = 'SubtitleIcon';\n\nexport default Subtitle;\n"
  },
  {
    "path": "app/assets/icons/SunIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M12 19C15.866 19 19 15.866 19 12C19 8.13401 15.866 5 12 5C8.13401 5 5 8.13401 5 12C5 15.866 8.13401 19 12 19Z\"\n      fill={color}\n    />\n    <path\n      d=\"M12 22.96C11.45 22.96 11 22.55 11 22V21.92C11 21.37 11.45 20.92 12 20.92C12.55 20.92 13 21.37 13 21.92C13 22.47 12.55 22.96 12 22.96ZM19.14 20.14C18.88 20.14 18.63 20.04 18.43 19.85L18.3 19.72C17.91 19.33 17.91 18.7 18.3 18.31C18.69 17.92 19.32 17.92 19.71 18.31L19.84 18.44C20.23 18.83 20.23 19.46 19.84 19.85C19.65 20.04 19.4 20.14 19.14 20.14ZM4.86 20.14C4.6 20.14 4.35 20.04 4.15 19.85C3.76 19.46 3.76 18.83 4.15 18.44L4.28 18.31C4.67 17.92 5.3 17.92 5.69 18.31C6.08 18.7 6.08 19.33 5.69 19.72L5.56 19.85C5.37 20.04 5.11 20.14 4.86 20.14ZM22 13H21.92C21.37 13 20.92 12.55 20.92 12C20.92 11.45 21.37 11 21.92 11C22.47 11 22.96 11.45 22.96 12C22.96 12.55 22.55 13 22 13ZM2.08 13H2C1.45 13 1 12.55 1 12C1 11.45 1.45 11 2 11C2.55 11 3.04 11.45 3.04 12C3.04 12.55 2.63 13 2.08 13ZM19.01 5.99C18.75 5.99 18.5 5.89 18.3 5.7C17.91 5.31 17.91 4.68 18.3 4.29L18.43 4.16C18.82 3.77 19.45 3.77 19.84 4.16C20.23 4.55 20.23 5.18 19.84 5.57L19.71 5.7C19.52 5.89 19.27 5.99 19.01 5.99ZM4.99 5.99C4.73 5.99 4.48 5.89 4.28 5.7L4.15 5.56C3.76 5.17 3.76 4.54 4.15 4.15C4.54 3.76 5.17 3.76 5.56 4.15L5.69 4.28C6.08 4.67 6.08 5.3 5.69 5.69C5.5 5.89 5.24 5.99 4.99 5.99ZM12 3.04C11.45 3.04 11 2.63 11 2.08V2C11 1.45 11.45 1 12 1C12.55 1 13 1.45 13 2C13 2.55 12.55 3.04 12 3.04Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M12 19.25C8 19.25 4.75 16 4.75 12C4.75 8 8 4.75 12 4.75C16 4.75 19.25 8 19.25 12C19.25 16 16 19.25 12 19.25ZM12 6.25C8.83 6.25 6.25 8.83 6.25 12C6.25 15.17 8.83 17.75 12 17.75C15.17 17.75 17.75 15.17 17.75 12C17.75 8.83 15.17 6.25 12 6.25Z\"\n      fill={color}\n    />\n    <path\n      d=\"M12 22.96C11.45 22.96 11 22.55 11 22V21.92C11 21.37 11.45 20.92 12 20.92C12.55 20.92 13 21.37 13 21.92C13 22.47 12.55 22.96 12 22.96ZM19.14 20.14C18.88 20.14 18.63 20.04 18.43 19.85L18.3 19.72C17.91 19.33 17.91 18.7 18.3 18.31C18.69 17.92 19.32 17.92 19.71 18.31L19.84 18.44C20.23 18.83 20.23 19.46 19.84 19.85C19.65 20.04 19.4 20.14 19.14 20.14ZM4.86 20.14C4.6 20.14 4.35 20.04 4.15 19.85C3.76 19.46 3.76 18.83 4.15 18.44L4.28 18.31C4.67 17.92 5.3 17.92 5.69 18.31C6.08 18.7 6.08 19.33 5.69 19.72L5.56 19.85C5.37 20.04 5.11 20.14 4.86 20.14ZM22 13H21.92C21.37 13 20.92 12.55 20.92 12C20.92 11.45 21.37 11 21.92 11C22.47 11 22.96 11.45 22.96 12C22.96 12.55 22.55 13 22 13ZM2.08 13H2C1.45 13 1 12.55 1 12C1 11.45 1.45 11 2 11C2.55 11 3.04 11.45 3.04 12C3.04 12.55 2.63 13 2.08 13ZM19.01 5.99C18.75 5.99 18.5 5.89 18.3 5.7C17.91 5.31 17.91 4.68 18.3 4.29L18.43 4.16C18.82 3.77 19.45 3.77 19.84 4.16C20.23 4.55 20.23 5.18 19.84 5.57L19.71 5.7C19.52 5.89 19.27 5.99 19.01 5.99ZM4.99 5.99C4.73 5.99 4.48 5.89 4.28 5.7L4.15 5.56C3.76 5.17 3.76 4.54 4.15 4.15C4.54 3.76 5.17 3.76 5.56 4.15L5.69 4.28C6.08 4.67 6.08 5.3 5.69 5.69C5.5 5.89 5.24 5.99 4.99 5.99ZM12 3.04C11.45 3.04 11 2.63 11 2.08V2C11 1.45 11.45 1 12 1C12.55 1 13 1.45 13 2C13 2.55 12.55 3.04 12 3.04Z\"\n      fill={color}\n    />\n  </g>\n);\n\ninterface ISunProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst Sun = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: ISunProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nSun.displayName = 'SunIcon';\n\nexport default Sun;\n"
  },
  {
    "path": "app/assets/icons/TickIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(2 2)\">\n    <path\n      d=\"M14.34,20H5.67C2.279,20,0,17.625,0,14.091V5.92C0,2.379,2.279,0,5.67,0h8.67C17.725,0,20,2.379,20,5.92v8.171C20,17.625,17.725,20,14.34,20ZM6.44,9.125a.875.875,0,0,0-.62,1.5L8.2,12.99a.881.881,0,0,0,1.23,0L14.18,8.24A.877.877,0,0,0,12.94,7L8.81,11.13,7.06,9.38A.868.868,0,0,0,6.44,9.125Z\"\n      transform=\"translate(0 0)\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g transform=\"translate(2 2)\">\n    <path\n      d=\"M0,2.373,2.374,4.746,7.12,0\"\n      transform=\"translate(6.44 7.627)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n  </g>\n);\n\ninterface ITickProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst Tick = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: ITickProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nTick.displayName = 'TickIcon';\n\nexport default Tick;\n"
  },
  {
    "path": "app/assets/icons/TrendingUpIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M16.19 2H7.81C4.17 2 2 4.17 2 7.81V16.18C2 19.83 4.17 22 7.81 22H16.18C19.82 22 21.99 19.83 21.99 16.19V7.81C22 4.17 19.83 2 16.19 2ZM16.88 11.53C16.88 11.92 16.57 12.23 16.18 12.23C15.79 12.23 15.48 11.92 15.48 11.53V11.35L12.76 14.07C12.61 14.22 12.41 14.29 12.2 14.27C11.99 14.25 11.8 14.14 11.69 13.96L10.67 12.44L8.29 14.82C8.15 14.96 7.98 15.02 7.8 15.02C7.62 15.02 7.44 14.95 7.31 14.82C7.04 14.55 7.04 14.11 7.31 13.83L10.29 10.85C10.44 10.7 10.64 10.63 10.85 10.65C11.06 10.67 11.25 10.78 11.36 10.96L12.38 12.48L14.49 10.37H14.31C13.92 10.37 13.61 10.06 13.61 9.67C13.61 9.28 13.92 8.97 14.31 8.97H16.17C16.26 8.97 16.35 8.99 16.44 9.02C16.61 9.09 16.75 9.23 16.82 9.4C16.86 9.49 16.87 9.58 16.87 9.67V11.53H16.88Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <g>\n    <path\n      d=\"M7.50043 15.2499C7.31043 15.2499 7.12043 15.1799 6.97043 15.0299C6.68043 14.7399 6.68043 14.2599 6.97043 13.9699L10.1704 10.7699C10.3304 10.6099 10.5404 10.5299 10.7704 10.5499C10.9904 10.5699 11.1904 10.6899 11.3204 10.8799L12.4104 12.5199L15.9604 8.96994C16.2504 8.67994 16.7304 8.67994 17.0204 8.96994C17.3104 9.25994 17.3104 9.73994 17.0204 10.0299L12.8204 14.2299C12.6604 14.3899 12.4504 14.4699 12.2204 14.4499C12.0004 14.4299 11.8004 14.3099 11.6704 14.1199L10.5804 12.4799L8.03043 15.0299C7.88043 15.1799 7.69043 15.2499 7.50043 15.2499Z\"\n      fill={color}\n    />\n    <path\n      d=\"M16.5 12.25C16.09 12.25 15.75 11.91 15.75 11.5V10.25H14.5C14.09 10.25 13.75 9.91 13.75 9.5C13.75 9.09 14.09 8.75 14.5 8.75H16.5C16.91 8.75 17.25 9.09 17.25 9.5V11.5C17.25 11.91 16.91 12.25 16.5 12.25Z\"\n      fill={color}\n    />\n    <path\n      d=\"M15 22.75H9C3.57 22.75 1.25 20.43 1.25 15V9C1.25 3.57 3.57 1.25 9 1.25H15C20.43 1.25 22.75 3.57 22.75 9V15C22.75 20.43 20.43 22.75 15 22.75ZM9 2.75C4.39 2.75 2.75 4.39 2.75 9V15C2.75 19.61 4.39 21.25 9 21.25H15C19.61 21.25 21.25 19.61 21.25 15V9C21.25 4.39 19.61 2.75 15 2.75H9Z\"\n      fill={color}\n    />\n  </g>\n);\n\ninterface ITrendingUpProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst TrendingUp = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: ITrendingUpProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nTrendingUp.displayName = 'TrendingUpIcon';\n\nexport default TrendingUp;\n"
  },
  {
    "path": "app/assets/icons/TvIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g>\n    <path\n      fill={color}\n      d=\"M4.75 4A2.75 2.75 0 0 0 2 6.75v8.5A2.75 2.75 0 0 0 4.75 18h14.5A2.75 2.75 0 0 0 22 15.25v-8.5A2.75 2.75 0 0 0 19.25 4H4.75ZM5 20.25a.75.75 0 0 1 .75-.75h12.5a.75.75 0 0 1 0 1.5H5.75a.75.75 0 0 1-.75-.75Z\"\n    />\n  </g>\n);\n\nconst Light = ({ color }: { color: string }) => (\n  <g>\n    <path\n      fill={color}\n      d=\"M4.75 4A2.75 2.75 0 0 0 2 6.75v8.5A2.75 2.75 0 0 0 4.75 18h14.5A2.75 2.75 0 0 0 22 15.25v-8.5A2.75 2.75 0 0 0 19.25 4H4.75ZM3.5 6.75c0-.69.56-1.25 1.25-1.25h14.5c.69 0 1.25.56 1.25 1.25v8.5c0 .69-.56 1.25-1.25 1.25H4.75c-.69 0-1.25-.56-1.25-1.25v-8.5ZM5.75 19.5a.75.75 0 0 0 0 1.5h12.5a.75.75 0 0 0 0-1.5H5.75Z\"\n    />\n  </g>\n);\n\ninterface ITvProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst TvShows = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: ITvProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nTvShows.displayName = 'TvShowsIcon';\n\nexport default TvShows;\n"
  },
  {
    "path": "app/assets/icons/TwoUsersIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(2 3)\">\n    <path\n      d=\"M0,14.918c0-2.447,3.386-3.06,7.349-3.06,3.985,0,7.349.634,7.349,3.083S11.313,18,7.349,18C3.364,18,0,17.366,0,14.918Zm16.633.475c.341-3.112-2.366-4.588-3.067-4.927a.053.053,0,0,1-.033-.054.041.041,0,0,1,.037-.028,18.394,18.394,0,0,1,3.748.319,3.193,3.193,0,0,1,2.462,1.468,2.106,2.106,0,0,1,0,1.877c-.532,1.123-2.246,1.485-2.912,1.578l-.03,0A.208.208,0,0,1,16.633,15.393ZM2.487,4.763A4.8,4.8,0,0,1,7.349,0a4.8,4.8,0,0,1,4.863,4.763A4.8,4.8,0,0,1,7.349,9.525,4.8,4.8,0,0,1,2.487,4.763ZM13.719,8.822a4.069,4.069,0,0,1-.56-.052.177.177,0,0,1-.122-.274,6.432,6.432,0,0,0-.1-7.439.11.11,0,0,1-.018-.123.148.148,0,0,1,.095-.056A4.2,4.2,0,0,1,13.834.8a4.045,4.045,0,0,1,3.957,5.076A4.04,4.04,0,0,1,13.83,8.823Z\"\n      fill={color}\n    />\n  </g>\n);\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g transform=\"translate(2 2.5)\">\n    <path\n      d=\"M0,6.594A3.3,3.3,0,0,0,3.3,3.3,3.3,3.3,0,0,0,0,0\"\n      transform=\"translate(14.02 1.819)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M0,0A10.9,10.9,0,0,1,1.617.233,2.664,2.664,0,0,1,3.562,1.346a1.568,1.568,0,0,1,0,1.345A2.683,2.683,0,0,1,1.617,3.809\"\n      transform=\"translate(15.536 11.996)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M6.841,0c3.69,0,6.842.559,6.842,2.792S10.551,5.6,6.841,5.6C3.151,5.6,0,5.046,0,2.812S3.131,0,6.841,0Z\"\n      transform=\"translate(0.75 12.706)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M4.384,8.769A4.385,4.385,0,1,1,8.769,4.384,4.369,4.369,0,0,1,4.384,8.769Z\"\n      transform=\"translate(3.207 0.75)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n  </g>\n);\n\ninterface ITwoUsersProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The class name of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst TwoUsers = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: ITwoUsersProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={2} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nTwoUsers.displayName = 'TwoUsersIcon';\n\nexport default TwoUsers;\n"
  },
  {
    "path": "app/assets/icons/UserIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(4 2)\">\n    <path\n      d=\"M0,16.575c0-2.722,3.686-3.4,8-3.4,4.339,0,8,.7,8,3.424S12.315,20,8,20C3.662,20,0,19.3,0,16.575ZM2.706,5.291A5.294,5.294,0,1,1,8,10.583,5.274,5.274,0,0,1,2.706,5.291Z\"\n      transform=\"translate(0 0)\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g transform=\"translate(4 2.5)\">\n    <circle\n      cx=\"4.778\"\n      cy=\"4.778\"\n      r=\"4.778\"\n      transform=\"translate(2.801 0)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M0,3.016a2.215,2.215,0,0,1,.22-.97A4.042,4.042,0,0,1,3.039.426,16.787,16.787,0,0,1,5.382.1,25.053,25.053,0,0,1,9.767.1a16.979,16.979,0,0,1,2.343.33c1.071.22,2.362.659,2.819,1.62a2.27,2.27,0,0,1,0,1.95c-.458.961-1.748,1.4-2.819,1.611a15.716,15.716,0,0,1-2.343.339A25.822,25.822,0,0,1,6.2,6a4.066,4.066,0,0,1-.815-.055,15.423,15.423,0,0,1-2.334-.339C1.968,5.4.687,4.957.22,4A2.279,2.279,0,0,1,0,3.016Z\"\n      transform=\"translate(0 13.185)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n  </g>\n);\n\ninterface IUserProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst User = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IUserProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 22 22\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nUser.displayName = 'UserIcon';\n\nexport default User;\n"
  },
  {
    "path": "app/assets/icons/ViewGridCardIcon.tsx",
    "content": "const Bold = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <>\n    <rect\n      x=\"6\"\n      y=\"6\"\n      width=\"36\"\n      height=\"36\"\n      rx=\"3\"\n      fill={color}\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <rect\n      x=\"13\"\n      y=\"13\"\n      width=\"8\"\n      height=\"8\"\n      fill=\"#FFF\"\n      stroke=\"#FFF\"\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <rect\n      x=\"27\"\n      y=\"13\"\n      width=\"8\"\n      height=\"8\"\n      fill=\"#FFF\"\n      stroke=\"#FFF\"\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <rect\n      x=\"13\"\n      y=\"27\"\n      width=\"8\"\n      height=\"8\"\n      fill=\"#FFF\"\n      stroke=\"#FFF\"\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <rect\n      x=\"27\"\n      y=\"27\"\n      width=\"8\"\n      height=\"8\"\n      fill=\"#FFF\"\n      stroke=\"#FFF\"\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n  </>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <>\n    <rect\n      x=\"13\"\n      y=\"13\"\n      width=\"8\"\n      height=\"8\"\n      fill=\"none\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <rect\n      x=\"27\"\n      y=\"13\"\n      width=\"8\"\n      height=\"8\"\n      fill=\"none\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <rect\n      x=\"13\"\n      y=\"27\"\n      width=\"8\"\n      height=\"8\"\n      fill=\"none\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <rect\n      x=\"27\"\n      y=\"27\"\n      width=\"8\"\n      height=\"8\"\n      fill=\"none\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n  </>\n);\n\ninterface IViewGridProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst ViewGridCard = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: IViewGridProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 48 48\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 48 48\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} strokeWidth={1.5} />;\n        </svg>\n      );\n  }\n};\n\nViewGridCard.displayName = 'ViewGridCardIcon';\n\nexport default ViewGridCard;\n"
  },
  {
    "path": "app/assets/icons/ViewGridDetailIcon.tsx",
    "content": "const Bold = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <>\n    <rect\n      x=\"6\"\n      y=\"6\"\n      width=\"36\"\n      height=\"36\"\n      rx=\"3\"\n      fill={color}\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <rect\n      x=\"13\"\n      y=\"13\"\n      width=\"8\"\n      height=\"8\"\n      fill=\"#FFF\"\n      stroke=\"#FFF\"\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M27 13L35 13\"\n      stroke=\"#FFF\"\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M27 20L35 20\"\n      stroke=\"#FFF\"\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M13 28L35 28\"\n      stroke=\"#FFF\"\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M13 35H35\"\n      stroke=\"#FFF\"\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n  </>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <>\n    <rect\n      x=\"13\"\n      y=\"13\"\n      width=\"8\"\n      height=\"8\"\n      fill=\"none\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M27 13L35 13\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M27 20L35 20\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M13 28L35 28\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M13 35H35\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n  </>\n);\n\ninterface IViewGridProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst ViewGridDetail = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: IViewGridProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 48 48\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 48 48\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} strokeWidth={1.5} />;\n        </svg>\n      );\n  }\n};\n\nViewGridDetail.displayName = 'ViewGridDetailIcon';\n\nexport default ViewGridDetail;\n"
  },
  {
    "path": "app/assets/icons/ViewGridTableIcon.tsx",
    "content": "const Bold = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <>\n    <rect\n      x=\"6\"\n      y=\"6\"\n      width=\"36\"\n      height=\"36\"\n      rx=\"3\"\n      fill={color}\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <rect\n      x=\"13\"\n      y=\"13\"\n      width=\"8\"\n      height=\"8\"\n      fill=\"#FFF\"\n      stroke=\"#FFF\"\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <rect\n      x=\"13\"\n      y=\"27\"\n      width=\"8\"\n      height=\"8\"\n      fill=\"#FFF\"\n      stroke=\"#FFF\"\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M27 28L35 28\"\n      stroke=\"#FFF\"\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M27 35H35\"\n      stroke=\"#FFF\"\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M27 13L35 13\"\n      stroke=\"#FFF\"\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M27 20L35 20\"\n      stroke=\"#FFF\"\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n  </>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <>\n    <rect\n      x=\"13\"\n      y=\"13\"\n      width=\"8\"\n      height=\"8\"\n      fill=\"none\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <rect\n      x=\"13\"\n      y=\"27\"\n      width=\"8\"\n      height=\"8\"\n      fill=\"none\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M27 28L35 28\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M27 35H35\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M27 13L35 13\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n    <path\n      d=\"M27 20L35 20\"\n      stroke={color}\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n  </>\n);\n\ninterface IViewGridTableProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n  /**\n   * The className of the icon\n   * @default ''\n   * @type string\n   * */\n  className?: string;\n}\n\nconst ViewGridTable = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  className = '',\n  ...props\n}: IViewGridTableProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className={className}\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 48 48\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 48 48\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} strokeWidth={1.5} />;\n        </svg>\n      );\n  }\n};\n\nViewGridTable.displayName = 'ViewGridTableIcon';\n\nexport default ViewGridTable;\n"
  },
  {
    "path": "app/assets/icons/VolumeOffIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(3 3)\">\n    <path\n      d=\"M.274,18.733a.929.929,0,0,1,0-1.314L3.1,14.593H3.068A2.637,2.637,0,0,1,.443,12.052a27.39,27.39,0,0,1,0-5.6A2.683,2.683,0,0,1,3.068,3.987H4.9L8.392,1.13A3.184,3.184,0,0,1,10.149.42a2.539,2.539,0,0,1,2.382,1.925,8.172,8.172,0,0,1,.234,1.472l.084.677c.014.1.026.2.037.309L17.42.271a.926.926,0,0,1,1.309,1.31L1.58,18.729A.947.947,0,0,1,.924,19,.964.964,0,0,1,.274,18.733Zm9.79-.572a2.746,2.746,0,0,1-1.6-.666l-1.3-1.006a.872.872,0,0,1-.071-1.294c.277-.338,4.648-4.322,4.792-4.463a.843.843,0,0,1,.607-.2c.389.1.464.656.458,1.094-.018,1.273-.06,2.157-.128,2.7l-.048.451a8.6,8.6,0,0,1-.228,1.432,2.546,2.546,0,0,1-2.372,1.952Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g transform=\"translate(3 2.5)\">\n    <path\n      d=\"M4.1,12.6H1.984A1.845,1.845,0,0,1,.141,10.753a25.917,25.917,0,0,1,0-5.406A1.9,1.9,0,0,1,1.984,3.568H4.1L7.809.53A2.278,2.278,0,0,1,9.039,0a1.742,1.742,0,0,1,1.651,1.363,14.268,14.268,0,0,1,.253,1.777A11.67,11.67,0,0,1,11.069,5.4\"\n      transform=\"translate(0.795 1.286)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M4.574,0c-.019,1.336-.065,2.149-.123,2.618A12.555,12.555,0,0,1,4.2,4.394,1.741,1.741,0,0,1,2.548,5.757,1.968,1.968,0,0,1,1.4,5.281L0,4.142\"\n      transform=\"translate(7.286 11.691)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M17.148,0,0,17.148\"\n      transform=\"translate(0.848 0.794)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n  </g>\n);\n\ninterface IVolumneOffProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst VolumeOff = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IVolumneOffProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nVolumeOff.displayName = 'VolumeOffIcon';\n\nexport default VolumeOff;\n"
  },
  {
    "path": "app/assets/icons/VolumeUpIcon.tsx",
    "content": "const Bold = ({ color }: { color: string }) => (\n  <g transform=\"translate(1 3)\">\n    <path\n      d=\"M9.677,18a2.693,2.693,0,0,1-1.588-.676l-3.526-2.94H2.752A2.645,2.645,0,0,1,.145,11.8a27.953,27.953,0,0,1,0-5.677,2.7,2.7,0,0,1,2.607-2.51H4.563l3.46-2.9A3.014,3.014,0,0,1,9.758,0a2.548,2.548,0,0,1,2.368,1.956,10.232,10.232,0,0,1,.231,1.495l.048.442a96.7,96.7,0,0,1,0,10.216l-.048.458a9.034,9.034,0,0,1-.224,1.453A2.54,2.54,0,0,1,9.782,18Zm8.614-.22a.957.957,0,0,1-.23-1.308A13.405,13.405,0,0,0,20.165,9,13.4,13.4,0,0,0,18.06,1.529.958.958,0,0,1,18.29.221a.9.9,0,0,1,1.277.236A15.319,15.319,0,0,1,22,9a15.318,15.318,0,0,1-2.433,8.544.912.912,0,0,1-.753.4A.9.9,0,0,1,18.291,17.78Zm-3.162-3.038a.957.957,0,0,1-.23-1.308A7.957,7.957,0,0,0,16.143,9,7.955,7.955,0,0,0,14.9,4.566a.957.957,0,0,1,.229-1.308.907.907,0,0,1,1.279.237A9.878,9.878,0,0,1,17.978,9a9.878,9.878,0,0,1-1.571,5.505.9.9,0,0,1-1.278.237Z\"\n      fill={color}\n    />\n  </g>\n);\n\nconst Light = ({ color, strokeWidth }: { color: string; strokeWidth: number }) => (\n  <g transform=\"translate(1.6 4)\">\n    <path\n      d=\"M10.871,12.892a12.254,12.254,0,0,1-.252,1.759A1.728,1.728,0,0,1,8.98,16a1.963,1.963,0,0,1-1.143-.471L4.07,12.469h-2.1A1.831,1.831,0,0,1,.141,10.644a25.553,25.553,0,0,1,0-5.35A1.882,1.882,0,0,1,1.972,3.532h2.1L7.759.525A2.264,2.264,0,0,1,8.98,0a1.729,1.729,0,0,1,1.639,1.349,13.9,13.9,0,0,1,.252,1.76A93.1,93.1,0,0,1,10.871,12.892Z\"\n      transform=\"translate(0 0.25)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M0,0A8.794,8.794,0,0,1,1.453,5.008,8.791,8.791,0,0,1,0,10.015\"\n      transform=\"translate(14.843 3.06)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n    <path\n      d=\"M0,0A14.165,14.165,0,0,1,2.341,8.068,14.169,14.169,0,0,1,0,16.137\"\n      transform=\"translate(18.274 0)\"\n      fill=\"none\"\n      stroke={color}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeMiterlimit=\"10\"\n      strokeWidth={strokeWidth}\n    />\n  </g>\n);\n\ninterface IVolumneUpProps {\n  /**\n   * The color of the icon\n   * @default 'currentColor'\n   */\n  fill?: string;\n  /**\n   * The fill of the icon\n   * @default false\n   * @type boolean\n   */\n  filled?: boolean;\n  /**\n   * The size of the icon\n   * @default 24\n   * @type number\n   */\n  size?: number;\n  /**\n   * The height of the icon\n   * @default 24\n   * @type number\n   */\n  height?: number;\n  /**\n   * The width of the icon\n   * @default 24\n   * @type number\n   * */\n  width?: number;\n}\n\nconst VolumeUp = ({\n  fill = 'currentColor',\n  filled = false,\n  size = 24,\n  height = 24,\n  width = 24,\n  ...props\n}: IVolumneUpProps) => {\n  switch (filled) {\n    case false:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Light color={fill} strokeWidth={1.5} />\n        </svg>\n      );\n    default:\n      return (\n        <svg\n          className=\"\"\n          width={width || size}\n          height={height || size}\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          style={{\n            display: 'inline',\n          }}\n          {...props}\n        >\n          <Bold color={fill} />;\n        </svg>\n      );\n  }\n};\n\nVolumeUp.displayName = 'VolumeUpIcon';\n\nexport default VolumeUp;\n"
  },
  {
    "path": "app/assets/lotties/external-link-black.json",
    "content": "{\n  \"v\": \"5.3.4\",\n  \"fr\": 60,\n  \"ip\": 0,\n  \"op\": 84,\n  \"w\": 678,\n  \"h\": 896,\n  \"nm\": \"share\",\n  \"ddd\": 0,\n  \"assets\": [],\n  \"layers\": [\n    {\n      \"ddd\": 0,\n      \"ind\": 1,\n      \"ty\": 4,\n      \"nm\": \"Слой 18\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.347,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p347_1_0p333_0\",\n              \"t\": 64,\n              \"s\": [338, 1288, 0],\n              \"e\": [338, 492, 0],\n              \"to\": [0, -132.66667175293, 0],\n              \"ti\": [0, 132.66667175293, 0]\n            },\n            {\n              \"t\": 76\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.354,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p354_1_0p333_0\",\n                    \"t\": 65,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [0.125, -433.461],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [0.229, -433.33],\n                          [0, -433.488],\n                          [0.25, -433.262]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [177, -256.711],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [-176.777, -256.711],\n                          [0, -433.488],\n                          [0, 169.488]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 79\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 50,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 2,\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 65,\n      \"op\": 84,\n      \"st\": 63,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 2,\n      \"ty\": 4,\n      \"nm\": \"Слой 15\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.393,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p393_1_0p333_0\",\n              \"t\": 5,\n              \"s\": [338, 492, 0],\n              \"e\": [338, 482, 0],\n              \"to\": [0, -1.66666662693024, 0],\n              \"ti\": [0, -22.1666660308838, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.418,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p418_1_0p333_0\",\n              \"t\": 15,\n              \"s\": [338, 482, 0],\n              \"e\": [338, 625, 0],\n              \"to\": [0, 22.1666660308838, 0],\n              \"ti\": [0, -1.66666662693024, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.362,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p362_1_0p333_0\",\n              \"t\": 26,\n              \"s\": [338, 625, 0],\n              \"e\": [338, 492, 0],\n              \"to\": [0, 1.66666662693024, 0],\n              \"ti\": [0, 22.1666660308838, 0]\n            },\n            {\n              \"t\": 35\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.338,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p338_1_0p333_0\",\n                    \"t\": 0,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [177, -256.711],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [-176.777, -256.711],\n                          [0, -433.488],\n                          [0, 169.488]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [169, -252.711],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [-168.777, -252.711],\n                          [0, -433.488],\n                          [0, 169.488]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.368,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p368_1_0p333_0\",\n                    \"t\": 10,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [169, -252.711],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [-168.777, -252.711],\n                          [0, -433.488],\n                          [0, 169.488]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [189, -181.274],\n                          [0.223, -273],\n                          [0, -273],\n                          [-198.777, -181.274],\n                          [0, -273],\n                          [0, 169.488]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.301,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p301_1_0p333_0\",\n                    \"t\": 21,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [189, -181.274],\n                          [0.223, -273],\n                          [0, -273],\n                          [-198.777, -181.274],\n                          [0, -273],\n                          [0, 169.488]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [177, -256.711],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [-176.777, -256.711],\n                          [0, -433.488],\n                          [0, 169.488]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.338,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p338_1_0p333_0\",\n                    \"t\": 30,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [177, -256.711],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [-176.777, -256.711],\n                          [0, -433.488],\n                          [0, 169.488]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [55, -331.686],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [-52.777, -331.843],\n                          [0, -433.488],\n                          [0, -78.512]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.301,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p301_1_0p333_0\",\n                    \"t\": 36,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [55, -331.686],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [-52.777, -331.843],\n                          [0, -433.488],\n                          [0, -78.512]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [0.25, -432.593],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [-0.027, -433.093],\n                          [0, -433.488],\n                          [0, -432.512]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 42\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 50,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 2,\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 39,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 3,\n      \"ty\": 4,\n      \"nm\": \"Слой 17\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [338, 492, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0, 0],\n                    [0, 0],\n                    [48, -30.133],\n                    [32.954, 0],\n                    [0, 0],\n                    [25.447, 15.225],\n                    [0, 61.848],\n                    [0, 0]\n                  ],\n                  \"o\": [\n                    [0, 0],\n                    [0, 60.546],\n                    [-26.126, 16.401],\n                    [0, 0],\n                    [-31.652, 0],\n                    [-49.724, -29.75],\n                    [0, 0],\n                    [0, 0]\n                  ],\n                  \"v\": [\n                    [301.777, 0],\n                    [301.777, 196],\n                    [221.699, 340.096],\n                    [131.777, 366],\n                    [-131.777, 366],\n                    [-218.579, 342.008],\n                    [-301.777, 196],\n                    [-301.777, 0]\n                  ],\n                  \"c\": false\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 50,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 2,\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        },\n        {\n          \"ty\": \"tm\",\n          \"s\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.833],\n                  \"y\": [1]\n                },\n                \"o\": {\n                  \"x\": [0.473],\n                  \"y\": [-0.005]\n                },\n                \"n\": [\"0p833_1_0p473_-0p005\"],\n                \"t\": 8,\n                \"s\": [0],\n                \"e\": [10]\n              },\n              {\n                \"i\": {\n                  \"x\": [0.411],\n                  \"y\": [0.948]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0]\n                },\n                \"n\": [\"0p411_0p948_0p167_0\"],\n                \"t\": 19,\n                \"s\": [10],\n                \"e\": [0]\n              },\n              {\n                \"t\": 40\n              }\n            ],\n            \"ix\": 1\n          },\n          \"e\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.833],\n                  \"y\": [1]\n                },\n                \"o\": {\n                  \"x\": [0.473],\n                  \"y\": [0.005]\n                },\n                \"n\": [\"0p833_1_0p473_0p005\"],\n                \"t\": 8,\n                \"s\": [100],\n                \"e\": [90]\n              },\n              {\n                \"i\": {\n                  \"x\": [0.411],\n                  \"y\": [1.052]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0]\n                },\n                \"n\": [\"0p411_1p052_0p167_0\"],\n                \"t\": 19,\n                \"s\": [90],\n                \"e\": [100]\n              },\n              {\n                \"t\": 40\n              }\n            ],\n            \"ix\": 2\n          },\n          \"o\": {\n            \"a\": 0,\n            \"k\": 0,\n            \"ix\": 3\n          },\n          \"m\": 1,\n          \"ix\": 2,\n          \"nm\": \"Trim Paths 1\",\n          \"mn\": \"ADBE Vector Filter - Trim\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 84,\n      \"st\": 0,\n      \"bm\": 0\n    }\n  ],\n  \"markers\": []\n}\n"
  },
  {
    "path": "app/assets/lotties/external-link-white.json",
    "content": "{\n  \"v\": \"5.3.4\",\n  \"fr\": 60,\n  \"ip\": 0,\n  \"op\": 84,\n  \"w\": 678,\n  \"h\": 896,\n  \"nm\": \"share\",\n  \"ddd\": 0,\n  \"assets\": [],\n  \"layers\": [\n    {\n      \"ddd\": 0,\n      \"ind\": 1,\n      \"ty\": 4,\n      \"nm\": \"Слой 18\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.347,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p347_1_0p333_0\",\n              \"t\": 64,\n              \"s\": [338, 1288, 0],\n              \"e\": [338, 492, 0],\n              \"to\": [0, -132.66667175293, 0],\n              \"ti\": [0, 132.66667175293, 0]\n            },\n            {\n              \"t\": 76\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.354,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p354_1_0p333_0\",\n                    \"t\": 65,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [0.125, -433.461],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [0.229, -433.33],\n                          [0, -433.488],\n                          [0.25, -433.262]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [177, -256.711],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [-176.777, -256.711],\n                          [0, -433.488],\n                          [0, 169.488]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 79\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 50,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 2,\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 65,\n      \"op\": 84,\n      \"st\": 63,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 2,\n      \"ty\": 4,\n      \"nm\": \"Слой 15\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.393,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p393_1_0p333_0\",\n              \"t\": 5,\n              \"s\": [338, 492, 0],\n              \"e\": [338, 482, 0],\n              \"to\": [0, -1.66666662693024, 0],\n              \"ti\": [0, -22.1666660308838, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.418,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p418_1_0p333_0\",\n              \"t\": 15,\n              \"s\": [338, 482, 0],\n              \"e\": [338, 625, 0],\n              \"to\": [0, 22.1666660308838, 0],\n              \"ti\": [0, -1.66666662693024, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.362,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p362_1_0p333_0\",\n              \"t\": 26,\n              \"s\": [338, 625, 0],\n              \"e\": [338, 492, 0],\n              \"to\": [0, 1.66666662693024, 0],\n              \"ti\": [0, 22.1666660308838, 0]\n            },\n            {\n              \"t\": 35\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.338,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p338_1_0p333_0\",\n                    \"t\": 0,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [177, -256.711],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [-176.777, -256.711],\n                          [0, -433.488],\n                          [0, 169.488]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [169, -252.711],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [-168.777, -252.711],\n                          [0, -433.488],\n                          [0, 169.488]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.368,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p368_1_0p333_0\",\n                    \"t\": 10,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [169, -252.711],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [-168.777, -252.711],\n                          [0, -433.488],\n                          [0, 169.488]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [189, -181.274],\n                          [0.223, -273],\n                          [0, -273],\n                          [-198.777, -181.274],\n                          [0, -273],\n                          [0, 169.488]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.301,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p301_1_0p333_0\",\n                    \"t\": 21,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [189, -181.274],\n                          [0.223, -273],\n                          [0, -273],\n                          [-198.777, -181.274],\n                          [0, -273],\n                          [0, 169.488]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [177, -256.711],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [-176.777, -256.711],\n                          [0, -433.488],\n                          [0, 169.488]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.338,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p338_1_0p333_0\",\n                    \"t\": 30,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [177, -256.711],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [-176.777, -256.711],\n                          [0, -433.488],\n                          [0, 169.488]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [55, -331.686],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [-52.777, -331.843],\n                          [0, -433.488],\n                          [0, -78.512]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.301,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p301_1_0p333_0\",\n                    \"t\": 36,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [55, -331.686],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [-52.777, -331.843],\n                          [0, -433.488],\n                          [0, -78.512]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [0.25, -432.593],\n                          [0.223, -433.488],\n                          [0, -433.488],\n                          [-0.027, -433.093],\n                          [0, -433.488],\n                          [0, -432.512]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 42\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 50,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 2,\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 39,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 3,\n      \"ty\": 4,\n      \"nm\": \"Слой 17\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [338, 492, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0, 0],\n                    [0, 0],\n                    [48, -30.133],\n                    [32.954, 0],\n                    [0, 0],\n                    [25.447, 15.225],\n                    [0, 61.848],\n                    [0, 0]\n                  ],\n                  \"o\": [\n                    [0, 0],\n                    [0, 60.546],\n                    [-26.126, 16.401],\n                    [0, 0],\n                    [-31.652, 0],\n                    [-49.724, -29.75],\n                    [0, 0],\n                    [0, 0]\n                  ],\n                  \"v\": [\n                    [301.777, 0],\n                    [301.777, 196],\n                    [221.699, 340.096],\n                    [131.777, 366],\n                    [-131.777, 366],\n                    [-218.579, 342.008],\n                    [-301.777, 196],\n                    [-301.777, 0]\n                  ],\n                  \"c\": false\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 50,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 2,\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        },\n        {\n          \"ty\": \"tm\",\n          \"s\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.833],\n                  \"y\": [1]\n                },\n                \"o\": {\n                  \"x\": [0.473],\n                  \"y\": [-0.005]\n                },\n                \"n\": [\"0p833_1_0p473_-0p005\"],\n                \"t\": 8,\n                \"s\": [0],\n                \"e\": [10]\n              },\n              {\n                \"i\": {\n                  \"x\": [0.411],\n                  \"y\": [0.948]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0]\n                },\n                \"n\": [\"0p411_0p948_0p167_0\"],\n                \"t\": 19,\n                \"s\": [10],\n                \"e\": [0]\n              },\n              {\n                \"t\": 40\n              }\n            ],\n            \"ix\": 1\n          },\n          \"e\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.833],\n                  \"y\": [1]\n                },\n                \"o\": {\n                  \"x\": [0.473],\n                  \"y\": [0.005]\n                },\n                \"n\": [\"0p833_1_0p473_0p005\"],\n                \"t\": 8,\n                \"s\": [100],\n                \"e\": [90]\n              },\n              {\n                \"i\": {\n                  \"x\": [0.411],\n                  \"y\": [1.052]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0]\n                },\n                \"n\": [\"0p411_1p052_0p167_0\"],\n                \"t\": 19,\n                \"s\": [90],\n                \"e\": [100]\n              },\n              {\n                \"t\": 40\n              }\n            ],\n            \"ix\": 2\n          },\n          \"o\": {\n            \"a\": 0,\n            \"k\": 0,\n            \"ix\": 3\n          },\n          \"m\": 1,\n          \"ix\": 2,\n          \"nm\": \"Trim Paths 1\",\n          \"mn\": \"ADBE Vector Filter - Trim\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 84,\n      \"st\": 0,\n      \"bm\": 0\n    }\n  ],\n  \"markers\": []\n}\n"
  },
  {
    "path": "app/assets/lotties/lottieflow-arrow-08-1-000000-easey.json",
    "content": "{\n  \"v\": \"5.3.4\",\n  \"fr\": 48,\n  \"ip\": 0,\n  \"op\": 72,\n  \"w\": 800,\n  \"h\": 500,\n  \"nm\": \"slider left\",\n  \"ddd\": 0,\n  \"assets\": [],\n  \"layers\": [\n    {\n      \"ddd\": 0,\n      \"ind\": 1,\n      \"ty\": 4,\n      \"nm\": \"arr\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 180,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 0,\n              \"s\": [164.782, 249.903, 0],\n              \"e\": [491.782, 249.903, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 16,\n              \"s\": [491.782, 249.903, 0],\n              \"e\": [-348.218, 249.903, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 66\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [235.218, 0.097, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0_1_0p333_0\",\n                    \"t\": 0,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [146.23, 215.255],\n                          [324.23, -1.036],\n                          [146.205, -215.061]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [168.23, 163.255],\n                          [324.23, -1.036],\n                          [167.838, -177.694]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_1_0p333_0\",\n                    \"t\": 16,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [168.23, 163.255],\n                          [324.23, -1.036],\n                          [167.838, -177.694]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [168.23, 3.255],\n                          [324.23, -1.036],\n                          [168.838, -1.694]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 34\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 60,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 2,\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 40,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 2,\n      \"ty\": 4,\n      \"nm\": \"line\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 180,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 0,\n              \"s\": [400, 250, 0],\n              \"e\": [449, 250, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 16,\n              \"s\": [449, 250, 0],\n              \"e\": [-391, 250, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 66\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0_1_0p333_0\",\n                    \"t\": 0,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [306.008, -0.33]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [46.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_1_0p333_0\",\n                    \"t\": 16,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [46.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-225.965, -0.66],\n                          [46.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 34\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 60,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 40,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 3,\n      \"ty\": 4,\n      \"nm\": \"arr 2\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 180,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.667,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p667_1_0p333_0\",\n              \"t\": 21,\n              \"s\": [491.782, 249.903, 0],\n              \"e\": [164.782, 249.903, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 58\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [235.218, 0.097, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 0.833\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_0p833_0p333_0\",\n                    \"t\": 21,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-43.77, -1.781],\n                          [-45.77, -0.071],\n                          [-46.162, -2.73]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-24.637, -0.642],\n                          [10.483, -1.777],\n                          [-26.499, -0.979]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.167,\n                      \"y\": 0.167\n                    },\n                    \"n\": \"0_1_0p167_0p167\",\n                    \"t\": 28,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-24.637, -0.642],\n                          [10.483, -1.777],\n                          [-26.499, -0.979]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [146.23, 215.255],\n                          [324.23, -1.036],\n                          [146.205, -215.061]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 58\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": [0],\n                      \"y\": [1]\n                    },\n                    \"o\": {\n                      \"x\": [0.333],\n                      \"y\": [0]\n                    },\n                    \"n\": [\"0_1_0p333_0\"],\n                    \"t\": 23,\n                    \"s\": [0],\n                    \"e\": [60]\n                  },\n                  {\n                    \"t\": 27\n                  }\n                ],\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 2,\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 25,\n      \"op\": 72,\n      \"st\": 21,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 4,\n      \"ty\": 4,\n      \"nm\": \"line 2\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 180,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.667,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p667_1_0p333_0\",\n              \"t\": 21,\n              \"s\": [449, 250, 0],\n              \"e\": [400, 250, 0],\n              \"to\": [-8.16666698455811, 0, 0],\n              \"ti\": [8.16666698455811, 0, 0]\n            },\n            {\n              \"t\": 64\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.411,\n                      \"y\": 0\n                    },\n                    \"n\": \"0_1_0p411_0\",\n                    \"t\": 20,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [-319.992, -1.32]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [282.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 71\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": [0],\n                      \"y\": [1]\n                    },\n                    \"o\": {\n                      \"x\": [0.333],\n                      \"y\": [0]\n                    },\n                    \"n\": [\"0_1_0p333_0\"],\n                    \"t\": 23,\n                    \"s\": [0],\n                    \"e\": [60]\n                  },\n                  {\n                    \"t\": 27\n                  }\n                ],\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 25,\n      \"op\": 72,\n      \"st\": 21,\n      \"bm\": 0\n    }\n  ],\n  \"markers\": [\n    {\n      \"tm\": 34,\n      \"cm\": \"1\",\n      \"dr\": 0\n    }\n  ]\n}\n"
  },
  {
    "path": "app/assets/lotties/lottieflow-arrow-08-1-0072F5-easey.json",
    "content": "{\n  \"v\": \"5.3.4\",\n  \"fr\": 48,\n  \"ip\": 0,\n  \"op\": 72,\n  \"w\": 800,\n  \"h\": 500,\n  \"nm\": \"slider left\",\n  \"ddd\": 0,\n  \"assets\": [],\n  \"layers\": [\n    {\n      \"ddd\": 0,\n      \"ind\": 1,\n      \"ty\": 4,\n      \"nm\": \"arr\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 180,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 0,\n              \"s\": [164.782, 249.903, 0],\n              \"e\": [491.782, 249.903, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 16,\n              \"s\": [491.782, 249.903, 0],\n              \"e\": [-348.218, 249.903, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 66\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [235.218, 0.097, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0_1_0p333_0\",\n                    \"t\": 0,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [146.23, 215.255],\n                          [324.23, -1.036],\n                          [146.205, -215.061]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [168.23, 163.255],\n                          [324.23, -1.036],\n                          [167.838, -177.694]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_1_0p333_0\",\n                    \"t\": 16,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [168.23, 163.255],\n                          [324.23, -1.036],\n                          [167.838, -177.694]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [168.23, 3.255],\n                          [324.23, -1.036],\n                          [168.838, -1.694]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 34\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0.447, 0.961, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 60,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 2,\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 40,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 2,\n      \"ty\": 4,\n      \"nm\": \"line\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 180,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 0,\n              \"s\": [400, 250, 0],\n              \"e\": [449, 250, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 16,\n              \"s\": [449, 250, 0],\n              \"e\": [-391, 250, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 66\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0_1_0p333_0\",\n                    \"t\": 0,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [306.008, -0.33]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [46.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_1_0p333_0\",\n                    \"t\": 16,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [46.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-225.965, -0.66],\n                          [46.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 34\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0.447, 0.961, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 60,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 40,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 3,\n      \"ty\": 4,\n      \"nm\": \"arr 2\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 180,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.667,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p667_1_0p333_0\",\n              \"t\": 21,\n              \"s\": [491.782, 249.903, 0],\n              \"e\": [164.782, 249.903, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 58\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [235.218, 0.097, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 0.833\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_0p833_0p333_0\",\n                    \"t\": 21,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-43.77, -1.781],\n                          [-45.77, -0.071],\n                          [-46.162, -2.73]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-24.637, -0.642],\n                          [10.483, -1.777],\n                          [-26.499, -0.979]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.167,\n                      \"y\": 0.167\n                    },\n                    \"n\": \"0_1_0p167_0p167\",\n                    \"t\": 28,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-24.637, -0.642],\n                          [10.483, -1.777],\n                          [-26.499, -0.979]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [146.23, 215.255],\n                          [324.23, -1.036],\n                          [146.205, -215.061]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 58\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0.447, 0.961, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": [0],\n                      \"y\": [1]\n                    },\n                    \"o\": {\n                      \"x\": [0.333],\n                      \"y\": [0]\n                    },\n                    \"n\": [\"0_1_0p333_0\"],\n                    \"t\": 23,\n                    \"s\": [0],\n                    \"e\": [60]\n                  },\n                  {\n                    \"t\": 27\n                  }\n                ],\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 2,\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 25,\n      \"op\": 72,\n      \"st\": 21,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 4,\n      \"ty\": 4,\n      \"nm\": \"line 2\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 180,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.667,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p667_1_0p333_0\",\n              \"t\": 21,\n              \"s\": [449, 250, 0],\n              \"e\": [400, 250, 0],\n              \"to\": [-8.16666698455811, 0, 0],\n              \"ti\": [8.16666698455811, 0, 0]\n            },\n            {\n              \"t\": 64\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.411,\n                      \"y\": 0\n                    },\n                    \"n\": \"0_1_0p411_0\",\n                    \"t\": 20,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [-319.992, -1.32]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [282.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 71\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0.447, 0.961, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": [0],\n                      \"y\": [1]\n                    },\n                    \"o\": {\n                      \"x\": [0.333],\n                      \"y\": [0]\n                    },\n                    \"n\": [\"0_1_0p333_0\"],\n                    \"t\": 23,\n                    \"s\": [0],\n                    \"e\": [60]\n                  },\n                  {\n                    \"t\": 27\n                  }\n                ],\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 25,\n      \"op\": 72,\n      \"st\": 21,\n      \"bm\": 0\n    }\n  ],\n  \"markers\": [\n    {\n      \"tm\": 34,\n      \"cm\": \"1\",\n      \"dr\": 0\n    }\n  ]\n}\n"
  },
  {
    "path": "app/assets/lotties/lottieflow-arrow-08-1-FFFFFF-easey.json",
    "content": "{\n  \"v\": \"5.3.4\",\n  \"fr\": 48,\n  \"ip\": 0,\n  \"op\": 72,\n  \"w\": 800,\n  \"h\": 500,\n  \"nm\": \"slider left\",\n  \"ddd\": 0,\n  \"assets\": [],\n  \"layers\": [\n    {\n      \"ddd\": 0,\n      \"ind\": 1,\n      \"ty\": 4,\n      \"nm\": \"arr\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 180,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 0,\n              \"s\": [164.782, 249.903, 0],\n              \"e\": [491.782, 249.903, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 16,\n              \"s\": [491.782, 249.903, 0],\n              \"e\": [-348.218, 249.903, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 66\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [235.218, 0.097, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0_1_0p333_0\",\n                    \"t\": 0,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [146.23, 215.255],\n                          [324.23, -1.036],\n                          [146.205, -215.061]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [168.23, 163.255],\n                          [324.23, -1.036],\n                          [167.838, -177.694]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_1_0p333_0\",\n                    \"t\": 16,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [168.23, 163.255],\n                          [324.23, -1.036],\n                          [167.838, -177.694]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [168.23, 3.255],\n                          [324.23, -1.036],\n                          [168.838, -1.694]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 34\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 60,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 2,\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 40,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 2,\n      \"ty\": 4,\n      \"nm\": \"line\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 180,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 0,\n              \"s\": [400, 250, 0],\n              \"e\": [449, 250, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 16,\n              \"s\": [449, 250, 0],\n              \"e\": [-391, 250, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 66\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0_1_0p333_0\",\n                    \"t\": 0,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [306.008, -0.33]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [46.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_1_0p333_0\",\n                    \"t\": 16,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [46.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-225.965, -0.66],\n                          [46.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 34\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 60,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 40,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 3,\n      \"ty\": 4,\n      \"nm\": \"arr 2\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 180,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.667,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p667_1_0p333_0\",\n              \"t\": 21,\n              \"s\": [491.782, 249.903, 0],\n              \"e\": [164.782, 249.903, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 58\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [235.218, 0.097, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 0.833\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_0p833_0p333_0\",\n                    \"t\": 21,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-43.77, -1.781],\n                          [-45.77, -0.071],\n                          [-46.162, -2.73]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-24.637, -0.642],\n                          [10.483, -1.777],\n                          [-26.499, -0.979]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.167,\n                      \"y\": 0.167\n                    },\n                    \"n\": \"0_1_0p167_0p167\",\n                    \"t\": 28,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-24.637, -0.642],\n                          [10.483, -1.777],\n                          [-26.499, -0.979]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [146.23, 215.255],\n                          [324.23, -1.036],\n                          [146.205, -215.061]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 58\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": [0],\n                      \"y\": [1]\n                    },\n                    \"o\": {\n                      \"x\": [0.333],\n                      \"y\": [0]\n                    },\n                    \"n\": [\"0_1_0p333_0\"],\n                    \"t\": 23,\n                    \"s\": [0],\n                    \"e\": [60]\n                  },\n                  {\n                    \"t\": 27\n                  }\n                ],\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 2,\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 25,\n      \"op\": 72,\n      \"st\": 21,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 4,\n      \"ty\": 4,\n      \"nm\": \"line 2\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 180,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.667,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p667_1_0p333_0\",\n              \"t\": 21,\n              \"s\": [449, 250, 0],\n              \"e\": [400, 250, 0],\n              \"to\": [-8.16666698455811, 0, 0],\n              \"ti\": [8.16666698455811, 0, 0]\n            },\n            {\n              \"t\": 64\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.411,\n                      \"y\": 0\n                    },\n                    \"n\": \"0_1_0p411_0\",\n                    \"t\": 20,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [-319.992, -1.32]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [282.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 71\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": [0],\n                      \"y\": [1]\n                    },\n                    \"o\": {\n                      \"x\": [0.333],\n                      \"y\": [0]\n                    },\n                    \"n\": [\"0_1_0p333_0\"],\n                    \"t\": 23,\n                    \"s\": [0],\n                    \"e\": [60]\n                  },\n                  {\n                    \"t\": 27\n                  }\n                ],\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 25,\n      \"op\": 72,\n      \"st\": 21,\n      \"bm\": 0\n    }\n  ],\n  \"markers\": [\n    {\n      \"tm\": 34,\n      \"cm\": \"1\",\n      \"dr\": 0\n    }\n  ]\n}\n"
  },
  {
    "path": "app/assets/lotties/lottieflow-arrow-08-2-000000-easey.json",
    "content": "{\n  \"v\": \"5.3.4\",\n  \"fr\": 48,\n  \"ip\": 0,\n  \"op\": 72,\n  \"w\": 800,\n  \"h\": 500,\n  \"nm\": \"slider\",\n  \"ddd\": 0,\n  \"assets\": [],\n  \"layers\": [\n    {\n      \"ddd\": 0,\n      \"ind\": 1,\n      \"ty\": 4,\n      \"nm\": \"arr\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 0,\n              \"s\": [635.218, 250.097, 0],\n              \"e\": [308.218, 250.097, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 16,\n              \"s\": [308.218, 250.097, 0],\n              \"e\": [1148.218, 250.097, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 66\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [235.218, 0.097, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0_1_0p333_0\",\n                    \"t\": 0,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [146.23, 215.255],\n                          [324.23, -1.036],\n                          [146.205, -215.061]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [168.23, 163.255],\n                          [324.23, -1.036],\n                          [167.838, -177.694]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_1_0p333_0\",\n                    \"t\": 16,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [168.23, 163.255],\n                          [324.23, -1.036],\n                          [167.838, -177.694]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [168.23, 3.255],\n                          [324.23, -1.036],\n                          [168.838, -1.694]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 34\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 60,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 2,\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 40,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 2,\n      \"ty\": 4,\n      \"nm\": \"line\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 0,\n              \"s\": [400, 250, 0],\n              \"e\": [351, 250, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 16,\n              \"s\": [351, 250, 0],\n              \"e\": [1191, 250, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 66\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0_1_0p333_0\",\n                    \"t\": 0,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [306.008, -0.33]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [46.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_1_0p333_0\",\n                    \"t\": 16,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [46.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-225.965, -0.66],\n                          [46.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 34\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 60,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 40,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 3,\n      \"ty\": 4,\n      \"nm\": \"arr 2\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.667,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p667_1_0p333_0\",\n              \"t\": 21,\n              \"s\": [308.218, 250.097, 0],\n              \"e\": [635.218, 250.097, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 58\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [235.218, 0.097, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 0.833\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_0p833_0p333_0\",\n                    \"t\": 21,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-43.77, -1.781],\n                          [-45.77, -0.071],\n                          [-46.162, -2.73]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-24.637, -0.642],\n                          [10.483, -1.777],\n                          [-26.499, -0.979]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.167,\n                      \"y\": 0.167\n                    },\n                    \"n\": \"0_1_0p167_0p167\",\n                    \"t\": 28,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-24.637, -0.642],\n                          [10.483, -1.777],\n                          [-26.499, -0.979]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [146.23, 215.255],\n                          [324.23, -1.036],\n                          [146.205, -215.061]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 58\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": [0],\n                      \"y\": [1]\n                    },\n                    \"o\": {\n                      \"x\": [0.333],\n                      \"y\": [0]\n                    },\n                    \"n\": [\"0_1_0p333_0\"],\n                    \"t\": 23,\n                    \"s\": [0],\n                    \"e\": [60]\n                  },\n                  {\n                    \"t\": 27\n                  }\n                ],\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 2,\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 25,\n      \"op\": 72,\n      \"st\": 21,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 4,\n      \"ty\": 4,\n      \"nm\": \"line 2\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.667,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p667_1_0p333_0\",\n              \"t\": 21,\n              \"s\": [351, 250, 0],\n              \"e\": [400, 250, 0],\n              \"to\": [8.16666698455811, 0, 0],\n              \"ti\": [-8.16666698455811, 0, 0]\n            },\n            {\n              \"t\": 64\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.411,\n                      \"y\": 0\n                    },\n                    \"n\": \"0_1_0p411_0\",\n                    \"t\": 20,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [-319.992, -1.32]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [282.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 71\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": [0],\n                      \"y\": [1]\n                    },\n                    \"o\": {\n                      \"x\": [0.333],\n                      \"y\": [0]\n                    },\n                    \"n\": [\"0_1_0p333_0\"],\n                    \"t\": 23,\n                    \"s\": [0],\n                    \"e\": [60]\n                  },\n                  {\n                    \"t\": 27\n                  }\n                ],\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 25,\n      \"op\": 72,\n      \"st\": 21,\n      \"bm\": 0\n    }\n  ],\n  \"markers\": [\n    {\n      \"tm\": 34,\n      \"cm\": \"1\",\n      \"dr\": 0\n    }\n  ]\n}\n"
  },
  {
    "path": "app/assets/lotties/lottieflow-arrow-08-2-FFFFFF-easey.json",
    "content": "{\n  \"v\": \"5.3.4\",\n  \"fr\": 48,\n  \"ip\": 0,\n  \"op\": 72,\n  \"w\": 800,\n  \"h\": 500,\n  \"nm\": \"slider\",\n  \"ddd\": 0,\n  \"assets\": [],\n  \"layers\": [\n    {\n      \"ddd\": 0,\n      \"ind\": 1,\n      \"ty\": 4,\n      \"nm\": \"arr\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 0,\n              \"s\": [635.218, 250.097, 0],\n              \"e\": [308.218, 250.097, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 16,\n              \"s\": [308.218, 250.097, 0],\n              \"e\": [1148.218, 250.097, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 66\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [235.218, 0.097, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0_1_0p333_0\",\n                    \"t\": 0,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [146.23, 215.255],\n                          [324.23, -1.036],\n                          [146.205, -215.061]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [168.23, 163.255],\n                          [324.23, -1.036],\n                          [167.838, -177.694]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_1_0p333_0\",\n                    \"t\": 16,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [168.23, 163.255],\n                          [324.23, -1.036],\n                          [167.838, -177.694]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [168.23, 3.255],\n                          [324.23, -1.036],\n                          [168.838, -1.694]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 34\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 60,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 2,\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 40,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 2,\n      \"ty\": 4,\n      \"nm\": \"line\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 0,\n              \"s\": [400, 250, 0],\n              \"e\": [351, 250, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 16,\n              \"s\": [351, 250, 0],\n              \"e\": [1191, 250, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 66\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0_1_0p333_0\",\n                    \"t\": 0,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [306.008, -0.33]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [46.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_1_0p333_0\",\n                    \"t\": 16,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [46.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-225.965, -0.66],\n                          [46.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 34\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 60,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 40,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 3,\n      \"ty\": 4,\n      \"nm\": \"arr 2\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.667,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p667_1_0p333_0\",\n              \"t\": 21,\n              \"s\": [308.218, 250.097, 0],\n              \"e\": [635.218, 250.097, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 58\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [235.218, 0.097, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 0.833\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_0p833_0p333_0\",\n                    \"t\": 21,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-43.77, -1.781],\n                          [-45.77, -0.071],\n                          [-46.162, -2.73]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-24.637, -0.642],\n                          [10.483, -1.777],\n                          [-26.499, -0.979]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.167,\n                      \"y\": 0.167\n                    },\n                    \"n\": \"0_1_0p167_0p167\",\n                    \"t\": 28,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-24.637, -0.642],\n                          [10.483, -1.777],\n                          [-26.499, -0.979]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [146.23, 215.255],\n                          [324.23, -1.036],\n                          [146.205, -215.061]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 58\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": [0],\n                      \"y\": [1]\n                    },\n                    \"o\": {\n                      \"x\": [0.333],\n                      \"y\": [0]\n                    },\n                    \"n\": [\"0_1_0p333_0\"],\n                    \"t\": 23,\n                    \"s\": [0],\n                    \"e\": [60]\n                  },\n                  {\n                    \"t\": 27\n                  }\n                ],\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 2,\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 25,\n      \"op\": 72,\n      \"st\": 21,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 4,\n      \"ty\": 4,\n      \"nm\": \"line 2\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.667,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p667_1_0p333_0\",\n              \"t\": 21,\n              \"s\": [351, 250, 0],\n              \"e\": [400, 250, 0],\n              \"to\": [8.16666698455811, 0, 0],\n              \"ti\": [-8.16666698455811, 0, 0]\n            },\n            {\n              \"t\": 64\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.411,\n                      \"y\": 0\n                    },\n                    \"n\": \"0_1_0p411_0\",\n                    \"t\": 20,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [-319.992, -1.32]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-321.965, -0.33],\n                          [282.008, -0.66]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 71\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": [0],\n                      \"y\": [1]\n                    },\n                    \"o\": {\n                      \"x\": [0.333],\n                      \"y\": [0]\n                    },\n                    \"n\": [\"0_1_0p333_0\"],\n                    \"t\": 23,\n                    \"s\": [0],\n                    \"e\": [60]\n                  },\n                  {\n                    \"t\": 27\n                  }\n                ],\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 25,\n      \"op\": 72,\n      \"st\": 21,\n      \"bm\": 0\n    }\n  ],\n  \"markers\": [\n    {\n      \"tm\": 34,\n      \"cm\": \"1\",\n      \"dr\": 0\n    }\n  ]\n}\n"
  },
  {
    "path": "app/assets/lotties/lottieflow-dropdown-03-0072F5-easey.json",
    "content": "{\n  \"v\": \"5.3.4\",\n  \"fr\": 48,\n  \"ip\": 0,\n  \"op\": 96,\n  \"w\": 1000,\n  \"h\": 1000,\n  \"nm\": \"Dropdown  Accordion\",\n  \"ddd\": 0,\n  \"assets\": [],\n  \"layers\": [\n    {\n      \"ddd\": 0,\n      \"ind\": 1,\n      \"ty\": 4,\n      \"nm\": \"ATTGARROW Outlines\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.135,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p135_1_0p333_0\",\n              \"t\": 0,\n              \"s\": [491.938, 499.081, 0],\n              \"e\": [491.938, 314.081, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.215,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.598,\n                \"y\": 0\n              },\n              \"n\": \"0p215_1_0p598_0\",\n              \"t\": 15,\n              \"s\": [491.938, 314.081, 0],\n              \"e\": [491.938, 674.081, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 28,\n              \"s\": [491.938, 674.081, 0],\n              \"e\": [491.938, 499.081, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.601,\n                \"y\": 0.601\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0.333\n              },\n              \"n\": \"0p601_0p601_0p333_0p333\",\n              \"t\": 36,\n              \"s\": [491.938, 499.081, 0],\n              \"e\": [491.938, 499.081, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 60,\n              \"s\": [491.938, 499.081, 0],\n              \"e\": [491.938, 674.081, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.279,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p279_1_0p333_0\",\n              \"t\": 70,\n              \"s\": [491.938, 674.081, 0],\n              \"e\": [491.938, 314.081, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 86,\n              \"s\": [491.938, 314.081, 0],\n              \"e\": [491.938, 499.081, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 95\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [500, 500, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.176,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p176_1_0p333_0\",\n                    \"t\": 5,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-222.5, 111.25],\n                          [0, -111.25],\n                          [222.5, 111.25]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-104.062, 19.075],\n                          [11.299, 95.396],\n                          [120.062, 19.436]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.099,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p099_1_0p333_0\",\n                    \"t\": 22,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-104.062, 19.075],\n                          [11.299, 95.396],\n                          [120.062, 19.436]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-218.5, -106.75],\n                          [10, 122.75],\n                          [232.5, -105.75]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0_1_0p333_0\",\n                    \"t\": 36,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-218.5, -106.75],\n                          [10, 122.75],\n                          [232.5, -105.75]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-218.5, -106.75],\n                          [10, 122.75],\n                          [232.5, -105.75]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.048,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p048_1_0p333_0\",\n                    \"t\": 60,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-218.5, -106.75],\n                          [10, 122.75],\n                          [232.5, -105.75]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-104.062, 19.075],\n                          [11.299, 95.396],\n                          [120.062, 19.436]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.043,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p043_1_0p333_0\",\n                    \"t\": 78,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-104.062, 19.075],\n                          [11.299, 95.396],\n                          [120.062, 19.436]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-222.5, 111.25],\n                          [0, -111.25],\n                          [222.5, 111.25]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 95\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0.447, 0.961, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 80,\n                \"ix\": 5\n              },\n              \"lc\": 3,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [500, 500],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 99,\n      \"st\": 0,\n      \"bm\": 0\n    }\n  ],\n  \"markers\": []\n}\n"
  },
  {
    "path": "app/assets/lotties/lottieflow-dropdown-08-0072F5-easey.json",
    "content": "{\n  \"v\": \"5.3.4\",\n  \"fr\": 48,\n  \"ip\": 0,\n  \"op\": 120,\n  \"w\": 400,\n  \"h\": 580,\n  \"nm\": \"drop2\",\n  \"ddd\": 0,\n  \"assets\": [],\n  \"layers\": [\n    {\n      \"ddd\": 0,\n      \"ind\": 1,\n      \"ty\": 4,\n      \"nm\": \"Слой 9\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": [0.667],\n                \"y\": [1]\n              },\n              \"o\": {\n                \"x\": [0.528],\n                \"y\": [0]\n              },\n              \"n\": [\"0p667_1_0p528_0\"],\n              \"t\": 24,\n              \"s\": [-180],\n              \"e\": [0]\n            },\n            {\n              \"i\": {\n                \"x\": [0.667],\n                \"y\": [1]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p667_1_0p333_0\"],\n              \"t\": 41,\n              \"s\": [0],\n              \"e\": [0]\n            },\n            {\n              \"i\": {\n                \"x\": [0.667],\n                \"y\": [1]\n              },\n              \"o\": {\n                \"x\": [0.636],\n                \"y\": [0]\n              },\n              \"n\": [\"0p667_1_0p636_0\"],\n              \"t\": 89,\n              \"s\": [0],\n              \"e\": [-180]\n            },\n            {\n              \"t\": 111\n            }\n          ],\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 0,\n              \"s\": [202, 250, 0],\n              \"e\": [200, 218, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.027,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p027_1_0p333_0\",\n              \"t\": 9,\n              \"s\": [200, 218, 0],\n              \"e\": [200, 389, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.267,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p267_1_0p333_0\",\n              \"t\": 24,\n              \"s\": [200, 389, 0],\n              \"e\": [200, 250, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 41,\n              \"s\": [200, 250, 0],\n              \"e\": [200, 290, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.392,\n                \"y\": 0.392\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0.333\n              },\n              \"n\": \"0p392_0p392_0p333_0p333\",\n              \"t\": 48,\n              \"s\": [200, 290, 0],\n              \"e\": [200, 290, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0_1_0p333_0\",\n              \"t\": 72,\n              \"s\": [200, 290, 0],\n              \"e\": [200, 250, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.279,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p279_1_0p333_0\",\n              \"t\": 79,\n              \"s\": [200, 250, 0],\n              \"e\": [200, 389, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.159,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p159_1_0p333_0\",\n              \"t\": 96,\n              \"s\": [200, 389, 0],\n              \"e\": [200, 218, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.067,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p067_1_0p333_0\",\n              \"t\": 109,\n              \"s\": [200, 218, 0],\n              \"e\": [202, 250, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 120\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [-2, -59.957, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0.392, -0.635],\n                    [0, 0],\n                    [-0.785, 0],\n                    [0, 0],\n                    [0.412, 0.668],\n                    [0, 0]\n                  ],\n                  \"o\": [\n                    [0, 0],\n                    [-0.412, 0.668],\n                    [0, 0],\n                    [0.785, 0],\n                    [0, 0],\n                    [-0.392, -0.635]\n                  ],\n                  \"v\": [\n                    [-0.854, -218.379],\n                    [-146.345, 17.412],\n                    [-145.492, 18.942],\n                    [145.492, 18.942],\n                    [146.345, 17.412],\n                    [0.854, -218.379]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0.447, 0.961, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 60,\n                \"ix\": 5\n              },\n              \"lc\": 1,\n              \"lj\": 2,\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 120,\n      \"st\": 0,\n      \"bm\": 0\n    }\n  ],\n  \"markers\": []\n}\n"
  },
  {
    "path": "app/assets/lotties/lottieflow-menu-nav-11-6-000000-easey.json",
    "content": "{\n  \"v\": \"5.3.4\",\n  \"fr\": 48,\n  \"ip\": 0,\n  \"op\": 156,\n  \"w\": 710,\n  \"h\": 710,\n  \"nm\": \"Menu6\",\n  \"ddd\": 0,\n  \"assets\": [],\n  \"layers\": [\n    {\n      \"ddd\": 0,\n      \"ind\": 1,\n      \"ty\": 4,\n      \"nm\": \"Слой 28\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": [0.833],\n                \"y\": [0.833]\n              },\n              \"o\": {\n                \"x\": [0.724],\n                \"y\": [-0.037]\n              },\n              \"n\": [\"0p833_0p833_0p724_-0p037\"],\n              \"t\": 96,\n              \"s\": [0],\n              \"e\": [45]\n            },\n            {\n              \"t\": 116\n            }\n          ],\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [332.328, 355, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.667,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.167,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p667_1_0p167_0\",\n                    \"t\": 96,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [32, -32],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [231, -209],\n                          [-187, 209]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [32, -32],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [209, -209],\n                          [-209, 209]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 0.833\n                    },\n                    \"o\": {\n                      \"x\": 0.658,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_0p833_0p658_0\",\n                    \"t\": 116,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [32, -32],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [209, -209],\n                          [-209, 209]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [-0.063, 0.063],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [15.619, -15.619],\n                          [16.444, -16.444]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 125\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 80,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 96,\n      \"op\": 125,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 2,\n      \"ty\": 4,\n      \"nm\": \"Слой 26\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": [0.833],\n                \"y\": [0.833]\n              },\n              \"o\": {\n                \"x\": [0.724],\n                \"y\": [0.037]\n              },\n              \"n\": [\"0p833_0p833_0p724_0p037\"],\n              \"t\": 96,\n              \"s\": [0],\n              \"e\": [-45]\n            },\n            {\n              \"t\": 116\n            }\n          ],\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [355, 355, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.249,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p249_1_0p333_0\",\n                    \"t\": 37,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-209, -209],\n                          [209, 209]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.667,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.167,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p667_1_0p167_0\",\n                    \"t\": 53,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-209, -209],\n                          [209, 209]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-209, -209],\n                          [209, 209]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 0.833\n                    },\n                    \"o\": {\n                      \"x\": 0.658,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_0p833_0p658_0\",\n                    \"t\": 116,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-209, -209],\n                          [209, 209]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [11.515, 14.142],\n                          [14.343, 16.971]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 125\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 80,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 37,\n      \"op\": 125,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 3,\n      \"ty\": 4,\n      \"nm\": \"Слой 30\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [355, 355, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [32, -32],\n                    [0, 0]\n                  ],\n                  \"o\": [\n                    [0, 0],\n                    [0, 0]\n                  ],\n                  \"v\": [\n                    [209, -209],\n                    [0, 0]\n                  ],\n                  \"c\": false\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 80,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        },\n        {\n          \"ty\": \"tm\",\n          \"s\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.325],\n                  \"y\": [1]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0]\n                },\n                \"n\": [\"0p325_1_0p167_0\"],\n                \"t\": 40,\n                \"s\": [0],\n                \"e\": [0]\n              },\n              {\n                \"t\": 60\n              }\n            ],\n            \"ix\": 1\n          },\n          \"e\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.325],\n                  \"y\": [1]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0.167]\n                },\n                \"n\": [\"0p325_1_0p167_0p167\"],\n                \"t\": 40,\n                \"s\": [0],\n                \"e\": [100]\n              },\n              {\n                \"t\": 60\n              }\n            ],\n            \"ix\": 2\n          },\n          \"o\": {\n            \"a\": 0,\n            \"k\": 0,\n            \"ix\": 3\n          },\n          \"m\": 1,\n          \"ix\": 2,\n          \"nm\": \"Trim Paths 1\",\n          \"mn\": \"ADBE Vector Filter - Trim\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 96,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 4,\n      \"ty\": 4,\n      \"nm\": \"Слой 27\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [354, 356, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0, 0],\n                    [0, 0]\n                  ],\n                  \"o\": [\n                    [0, 0],\n                    [0, 0]\n                  ],\n                  \"v\": [\n                    [-209, 209],\n                    [1, -1]\n                  ],\n                  \"c\": false\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 80,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        },\n        {\n          \"ty\": \"tm\",\n          \"s\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.325],\n                  \"y\": [1]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0]\n                },\n                \"n\": [\"0p325_1_0p167_0\"],\n                \"t\": 40,\n                \"s\": [0],\n                \"e\": [0]\n              },\n              {\n                \"t\": 60\n              }\n            ],\n            \"ix\": 1\n          },\n          \"e\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.325],\n                  \"y\": [1]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0.167]\n                },\n                \"n\": [\"0p325_1_0p167_0p167\"],\n                \"t\": 40,\n                \"s\": [0],\n                \"e\": [100]\n              },\n              {\n                \"t\": 60\n              }\n            ],\n            \"ix\": 2\n          },\n          \"o\": {\n            \"a\": 0,\n            \"k\": 0,\n            \"ix\": 3\n          },\n          \"m\": 1,\n          \"ix\": 2,\n          \"nm\": \"Trim Paths 1\",\n          \"mn\": \"ADBE Vector Filter - Trim\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 96,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 5,\n      \"ty\": 4,\n      \"nm\": \"Слой 29\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [355, 355, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0, -172.718],\n                    [172.718, 0],\n                    [0, 172.718],\n                    [-172.718, 0]\n                  ],\n                  \"o\": [\n                    [0, 172.718],\n                    [-172.718, 0],\n                    [0, -172.718],\n                    [172.718, 0]\n                  ],\n                  \"v\": [\n                    [312.734, 0],\n                    [0, 312.734],\n                    [-312.734, 0],\n                    [0, -312.734]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 80,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        },\n        {\n          \"ty\": \"tm\",\n          \"s\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.366],\n                  \"y\": [1.002]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0.148]\n                },\n                \"n\": [\"0p366_1p002_0p167_0p148\"],\n                \"t\": 36,\n                \"s\": [0],\n                \"e\": [26.2]\n              },\n              {\n                \"t\": 44\n              }\n            ],\n            \"ix\": 1\n          },\n          \"e\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.833],\n                  \"y\": [0.833]\n                },\n                \"o\": {\n                  \"x\": [0.768],\n                  \"y\": [-0.014]\n                },\n                \"n\": [\"0p833_0p833_0p768_-0p014\"],\n                \"t\": 24,\n                \"s\": [0.2],\n                \"e\": [26.2]\n              },\n              {\n                \"t\": 40\n              }\n            ],\n            \"ix\": 2\n          },\n          \"o\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.833],\n                  \"y\": [0.889]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0.167]\n                },\n                \"n\": [\"0p833_0p889_0p167_0p167\"],\n                \"t\": 24,\n                \"s\": [-90],\n                \"e\": [41]\n              },\n              {\n                \"t\": 40\n              }\n            ],\n            \"ix\": 3\n          },\n          \"m\": 1,\n          \"ix\": 2,\n          \"nm\": \"Trim Paths 1\",\n          \"mn\": \"ADBE Vector Filter - Trim\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 24,\n      \"op\": 44,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 6,\n      \"ty\": 4,\n      \"nm\": \"Слой 25\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [355, 355, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0, -172.718],\n                    [172.718, 0],\n                    [0, 172.718],\n                    [-172.718, 0]\n                  ],\n                  \"o\": [\n                    [0, 172.718],\n                    [-172.718, 0],\n                    [0, -172.718],\n                    [172.718, 0]\n                  ],\n                  \"v\": [\n                    [312.734, 0],\n                    [0, 312.734],\n                    [-312.734, 0],\n                    [0, -312.734]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 80,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        },\n        {\n          \"ty\": \"tm\",\n          \"s\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.366],\n                  \"y\": [1.002]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0.148]\n                },\n                \"n\": [\"0p366_1p002_0p167_0p148\"],\n                \"t\": 36,\n                \"s\": [0],\n                \"e\": [26.2]\n              },\n              {\n                \"t\": 44\n              }\n            ],\n            \"ix\": 1\n          },\n          \"e\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.833],\n                  \"y\": [0.833]\n                },\n                \"o\": {\n                  \"x\": [0.768],\n                  \"y\": [-0.014]\n                },\n                \"n\": [\"0p833_0p833_0p768_-0p014\"],\n                \"t\": 24,\n                \"s\": [0.2],\n                \"e\": [26.2]\n              },\n              {\n                \"t\": 40\n              }\n            ],\n            \"ix\": 2\n          },\n          \"o\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.833],\n                  \"y\": [0.889]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0.167]\n                },\n                \"n\": [\"0p833_0p889_0p167_0p167\"],\n                \"t\": 24,\n                \"s\": [90],\n                \"e\": [221]\n              },\n              {\n                \"t\": 40\n              }\n            ],\n            \"ix\": 3\n          },\n          \"m\": 1,\n          \"ix\": 2,\n          \"nm\": \"Trim Paths 1\",\n          \"mn\": \"ADBE Vector Filter - Trim\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 24,\n      \"op\": 44,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 7,\n      \"ty\": 4,\n      \"nm\": \"Слой 31\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": [0.289],\n                \"y\": [0.995]\n              },\n              \"o\": {\n                \"x\": [0.167],\n                \"y\": [0.167]\n              },\n              \"n\": [\"0p289_0p995_0p167_0p167\"],\n              \"t\": 123,\n              \"s\": [-90],\n              \"e\": [0]\n            },\n            {\n              \"t\": 152\n            }\n          ],\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [355, 355, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0, -22.091],\n                    [22.091, 0],\n                    [0, 22.091],\n                    [-22.091, 0]\n                  ],\n                  \"o\": [\n                    [0, 22.091],\n                    [-22.091, 0],\n                    [0, -22.091],\n                    [22.091, 0]\n                  ],\n                  \"v\": [\n                    [40, 176.333],\n                    [0, 216.333],\n                    [-40, 176.333],\n                    [0, 136.333]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 1,\n                \"ix\": 5\n              },\n              \"lc\": 1,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 3,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 122,\n      \"op\": 156,\n      \"st\": 122,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 8,\n      \"ty\": 4,\n      \"nm\": \"Слой 32\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": [0.289],\n                \"y\": [0.995]\n              },\n              \"o\": {\n                \"x\": [0.167],\n                \"y\": [0.167]\n              },\n              \"n\": [\"0p289_0p995_0p167_0p167\"],\n              \"t\": 123,\n              \"s\": [-90],\n              \"e\": [0]\n            },\n            {\n              \"t\": 152\n            }\n          ],\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [355, 355, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0, -22.091],\n                    [22.091, 0],\n                    [0, 22.091],\n                    [-22.091, 0]\n                  ],\n                  \"o\": [\n                    [0, 22.091],\n                    [-22.091, 0],\n                    [0, -22.091],\n                    [22.091, 0]\n                  ],\n                  \"v\": [\n                    [40, -177],\n                    [0, -137],\n                    [-40, -177],\n                    [0, -217]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 1,\n                \"ix\": 5\n              },\n              \"lc\": 1,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 3,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 122,\n      \"op\": 156,\n      \"st\": 122,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 9,\n      \"ty\": 4,\n      \"nm\": \"Слой 24\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.148,\n                \"y\": 0.956\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p148_0p956_0p333_0\",\n              \"t\": 0,\n              \"s\": [355, 531.333, 0],\n              \"e\": [355, 475.167, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.833,\n                \"y\": 0.833\n              },\n              \"o\": {\n                \"x\": 0.673,\n                \"y\": 0\n              },\n              \"n\": \"0p833_0p833_0p673_0\",\n              \"t\": 11,\n              \"s\": [355, 475.167, 0],\n              \"e\": [355, 684.167, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 24\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 176.333, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0, -22.091],\n                    [22.091, 0],\n                    [0, 22.091],\n                    [-22.091, 0]\n                  ],\n                  \"o\": [\n                    [0, 22.091],\n                    [-22.091, 0],\n                    [0, -22.091],\n                    [22.091, 0]\n                  ],\n                  \"v\": [\n                    [40, 176.333],\n                    [0, 216.333],\n                    [-40, 176.333],\n                    [0, 136.333]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 1,\n                \"ix\": 5\n              },\n              \"lc\": 1,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 3,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 25,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 10,\n      \"ty\": 4,\n      \"nm\": \"Слой 23\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [355, 355, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0, -22.091],\n                    [22.091, 0],\n                    [0, 22.091],\n                    [-22.091, 0]\n                  ],\n                  \"o\": [\n                    [0, 22.091],\n                    [-22.091, 0],\n                    [0, -22.091],\n                    [22.091, 0]\n                  ],\n                  \"v\": [\n                    [40, 0],\n                    [0, 40],\n                    [-40, 0],\n                    [0, -40]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 1,\n                \"ix\": 5\n              },\n              \"lc\": 1,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 3,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 156,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 11,\n      \"ty\": 4,\n      \"nm\": \"Слой 22\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.148,\n                \"y\": 0.964\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p148_0p964_0p333_0\",\n              \"t\": 0,\n              \"s\": [355, 178, 0],\n              \"e\": [355, 246.5, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.833,\n                \"y\": 0.833\n              },\n              \"o\": {\n                \"x\": 0.673,\n                \"y\": 0\n              },\n              \"n\": \"0p833_0p833_0p673_0\",\n              \"t\": 11,\n              \"s\": [355, 246.5, 0],\n              \"e\": [355, 54.5, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 24\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, -177, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0, -22.091],\n                    [22.091, 0],\n                    [0, 22.091],\n                    [-22.091, 0]\n                  ],\n                  \"o\": [\n                    [0, 22.091],\n                    [-22.091, 0],\n                    [0, -22.091],\n                    [22.091, 0]\n                  ],\n                  \"v\": [\n                    [40, -177],\n                    [0, -137],\n                    [-40, -177],\n                    [0, -217]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 1,\n                \"ix\": 5\n              },\n              \"lc\": 1,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 3,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 25,\n      \"st\": 0,\n      \"bm\": 0\n    }\n  ],\n  \"markers\": []\n}\n"
  },
  {
    "path": "app/assets/lotties/lottieflow-menu-nav-11-6-FFFFFF-easey.json",
    "content": "{\n  \"v\": \"5.3.4\",\n  \"fr\": 48,\n  \"ip\": 0,\n  \"op\": 156,\n  \"w\": 710,\n  \"h\": 710,\n  \"nm\": \"Menu6\",\n  \"ddd\": 0,\n  \"assets\": [],\n  \"layers\": [\n    {\n      \"ddd\": 0,\n      \"ind\": 1,\n      \"ty\": 4,\n      \"nm\": \"Слой 28\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": [0.833],\n                \"y\": [0.833]\n              },\n              \"o\": {\n                \"x\": [0.724],\n                \"y\": [-0.037]\n              },\n              \"n\": [\"0p833_0p833_0p724_-0p037\"],\n              \"t\": 96,\n              \"s\": [0],\n              \"e\": [45]\n            },\n            {\n              \"t\": 116\n            }\n          ],\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [332.328, 355, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.667,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.167,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p667_1_0p167_0\",\n                    \"t\": 96,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [32, -32],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [231, -209],\n                          [-187, 209]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [32, -32],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [209, -209],\n                          [-209, 209]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 0.833\n                    },\n                    \"o\": {\n                      \"x\": 0.658,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_0p833_0p658_0\",\n                    \"t\": 116,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [32, -32],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [209, -209],\n                          [-209, 209]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [-0.063, 0.063],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [15.619, -15.619],\n                          [16.444, -16.444]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 125\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 80,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 96,\n      \"op\": 125,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 2,\n      \"ty\": 4,\n      \"nm\": \"Слой 26\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": [0.833],\n                \"y\": [0.833]\n              },\n              \"o\": {\n                \"x\": [0.724],\n                \"y\": [0.037]\n              },\n              \"n\": [\"0p833_0p833_0p724_0p037\"],\n              \"t\": 96,\n              \"s\": [0],\n              \"e\": [-45]\n            },\n            {\n              \"t\": 116\n            }\n          ],\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [355, 355, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.249,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p249_1_0p333_0\",\n                    \"t\": 37,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-209, -209],\n                          [209, 209]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.667,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.167,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p667_1_0p167_0\",\n                    \"t\": 53,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-209, -209],\n                          [209, 209]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-209, -209],\n                          [209, 209]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 0.833\n                    },\n                    \"o\": {\n                      \"x\": 0.658,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_0p833_0p658_0\",\n                    \"t\": 116,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-209, -209],\n                          [209, 209]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"o\": [\n                          [0, 0],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [11.515, 14.142],\n                          [14.343, 16.971]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 125\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 80,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 37,\n      \"op\": 125,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 3,\n      \"ty\": 4,\n      \"nm\": \"Слой 30\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [355, 355, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [32, -32],\n                    [0, 0]\n                  ],\n                  \"o\": [\n                    [0, 0],\n                    [0, 0]\n                  ],\n                  \"v\": [\n                    [209, -209],\n                    [0, 0]\n                  ],\n                  \"c\": false\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 80,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        },\n        {\n          \"ty\": \"tm\",\n          \"s\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.325],\n                  \"y\": [1]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0]\n                },\n                \"n\": [\"0p325_1_0p167_0\"],\n                \"t\": 40,\n                \"s\": [0],\n                \"e\": [0]\n              },\n              {\n                \"t\": 60\n              }\n            ],\n            \"ix\": 1\n          },\n          \"e\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.325],\n                  \"y\": [1]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0.167]\n                },\n                \"n\": [\"0p325_1_0p167_0p167\"],\n                \"t\": 40,\n                \"s\": [0],\n                \"e\": [100]\n              },\n              {\n                \"t\": 60\n              }\n            ],\n            \"ix\": 2\n          },\n          \"o\": {\n            \"a\": 0,\n            \"k\": 0,\n            \"ix\": 3\n          },\n          \"m\": 1,\n          \"ix\": 2,\n          \"nm\": \"Trim Paths 1\",\n          \"mn\": \"ADBE Vector Filter - Trim\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 96,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 4,\n      \"ty\": 4,\n      \"nm\": \"Слой 27\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [354, 356, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0, 0],\n                    [0, 0]\n                  ],\n                  \"o\": [\n                    [0, 0],\n                    [0, 0]\n                  ],\n                  \"v\": [\n                    [-209, 209],\n                    [1, -1]\n                  ],\n                  \"c\": false\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 80,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        },\n        {\n          \"ty\": \"tm\",\n          \"s\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.325],\n                  \"y\": [1]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0]\n                },\n                \"n\": [\"0p325_1_0p167_0\"],\n                \"t\": 40,\n                \"s\": [0],\n                \"e\": [0]\n              },\n              {\n                \"t\": 60\n              }\n            ],\n            \"ix\": 1\n          },\n          \"e\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.325],\n                  \"y\": [1]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0.167]\n                },\n                \"n\": [\"0p325_1_0p167_0p167\"],\n                \"t\": 40,\n                \"s\": [0],\n                \"e\": [100]\n              },\n              {\n                \"t\": 60\n              }\n            ],\n            \"ix\": 2\n          },\n          \"o\": {\n            \"a\": 0,\n            \"k\": 0,\n            \"ix\": 3\n          },\n          \"m\": 1,\n          \"ix\": 2,\n          \"nm\": \"Trim Paths 1\",\n          \"mn\": \"ADBE Vector Filter - Trim\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 96,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 5,\n      \"ty\": 4,\n      \"nm\": \"Слой 29\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [355, 355, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0, -172.718],\n                    [172.718, 0],\n                    [0, 172.718],\n                    [-172.718, 0]\n                  ],\n                  \"o\": [\n                    [0, 172.718],\n                    [-172.718, 0],\n                    [0, -172.718],\n                    [172.718, 0]\n                  ],\n                  \"v\": [\n                    [312.734, 0],\n                    [0, 312.734],\n                    [-312.734, 0],\n                    [0, -312.734]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 80,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        },\n        {\n          \"ty\": \"tm\",\n          \"s\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.366],\n                  \"y\": [1.002]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0.148]\n                },\n                \"n\": [\"0p366_1p002_0p167_0p148\"],\n                \"t\": 36,\n                \"s\": [0],\n                \"e\": [26.2]\n              },\n              {\n                \"t\": 44\n              }\n            ],\n            \"ix\": 1\n          },\n          \"e\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.833],\n                  \"y\": [0.833]\n                },\n                \"o\": {\n                  \"x\": [0.768],\n                  \"y\": [-0.014]\n                },\n                \"n\": [\"0p833_0p833_0p768_-0p014\"],\n                \"t\": 24,\n                \"s\": [0.2],\n                \"e\": [26.2]\n              },\n              {\n                \"t\": 40\n              }\n            ],\n            \"ix\": 2\n          },\n          \"o\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.833],\n                  \"y\": [0.889]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0.167]\n                },\n                \"n\": [\"0p833_0p889_0p167_0p167\"],\n                \"t\": 24,\n                \"s\": [-90],\n                \"e\": [41]\n              },\n              {\n                \"t\": 40\n              }\n            ],\n            \"ix\": 3\n          },\n          \"m\": 1,\n          \"ix\": 2,\n          \"nm\": \"Trim Paths 1\",\n          \"mn\": \"ADBE Vector Filter - Trim\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 24,\n      \"op\": 44,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 6,\n      \"ty\": 4,\n      \"nm\": \"Слой 25\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [355, 355, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0, -172.718],\n                    [172.718, 0],\n                    [0, 172.718],\n                    [-172.718, 0]\n                  ],\n                  \"o\": [\n                    [0, 172.718],\n                    [-172.718, 0],\n                    [0, -172.718],\n                    [172.718, 0]\n                  ],\n                  \"v\": [\n                    [312.734, 0],\n                    [0, 312.734],\n                    [-312.734, 0],\n                    [0, -312.734]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 80,\n                \"ix\": 5\n              },\n              \"lc\": 2,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        },\n        {\n          \"ty\": \"tm\",\n          \"s\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.366],\n                  \"y\": [1.002]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0.148]\n                },\n                \"n\": [\"0p366_1p002_0p167_0p148\"],\n                \"t\": 36,\n                \"s\": [0],\n                \"e\": [26.2]\n              },\n              {\n                \"t\": 44\n              }\n            ],\n            \"ix\": 1\n          },\n          \"e\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.833],\n                  \"y\": [0.833]\n                },\n                \"o\": {\n                  \"x\": [0.768],\n                  \"y\": [-0.014]\n                },\n                \"n\": [\"0p833_0p833_0p768_-0p014\"],\n                \"t\": 24,\n                \"s\": [0.2],\n                \"e\": [26.2]\n              },\n              {\n                \"t\": 40\n              }\n            ],\n            \"ix\": 2\n          },\n          \"o\": {\n            \"a\": 1,\n            \"k\": [\n              {\n                \"i\": {\n                  \"x\": [0.833],\n                  \"y\": [0.889]\n                },\n                \"o\": {\n                  \"x\": [0.167],\n                  \"y\": [0.167]\n                },\n                \"n\": [\"0p833_0p889_0p167_0p167\"],\n                \"t\": 24,\n                \"s\": [90],\n                \"e\": [221]\n              },\n              {\n                \"t\": 40\n              }\n            ],\n            \"ix\": 3\n          },\n          \"m\": 1,\n          \"ix\": 2,\n          \"nm\": \"Trim Paths 1\",\n          \"mn\": \"ADBE Vector Filter - Trim\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 24,\n      \"op\": 44,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 7,\n      \"ty\": 4,\n      \"nm\": \"Слой 31\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": [0.289],\n                \"y\": [0.995]\n              },\n              \"o\": {\n                \"x\": [0.167],\n                \"y\": [0.167]\n              },\n              \"n\": [\"0p289_0p995_0p167_0p167\"],\n              \"t\": 123,\n              \"s\": [-90],\n              \"e\": [0]\n            },\n            {\n              \"t\": 152\n            }\n          ],\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [355, 355, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0, -22.091],\n                    [22.091, 0],\n                    [0, 22.091],\n                    [-22.091, 0]\n                  ],\n                  \"o\": [\n                    [0, 22.091],\n                    [-22.091, 0],\n                    [0, -22.091],\n                    [22.091, 0]\n                  ],\n                  \"v\": [\n                    [40, 176.333],\n                    [0, 216.333],\n                    [-40, 176.333],\n                    [0, 136.333]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 1,\n                \"ix\": 5\n              },\n              \"lc\": 1,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 3,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 122,\n      \"op\": 156,\n      \"st\": 122,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 8,\n      \"ty\": 4,\n      \"nm\": \"Слой 32\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": [0.289],\n                \"y\": [0.995]\n              },\n              \"o\": {\n                \"x\": [0.167],\n                \"y\": [0.167]\n              },\n              \"n\": [\"0p289_0p995_0p167_0p167\"],\n              \"t\": 123,\n              \"s\": [-90],\n              \"e\": [0]\n            },\n            {\n              \"t\": 152\n            }\n          ],\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [355, 355, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0, -22.091],\n                    [22.091, 0],\n                    [0, 22.091],\n                    [-22.091, 0]\n                  ],\n                  \"o\": [\n                    [0, 22.091],\n                    [-22.091, 0],\n                    [0, -22.091],\n                    [22.091, 0]\n                  ],\n                  \"v\": [\n                    [40, -177],\n                    [0, -137],\n                    [-40, -177],\n                    [0, -217]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 1,\n                \"ix\": 5\n              },\n              \"lc\": 1,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 3,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 122,\n      \"op\": 156,\n      \"st\": 122,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 9,\n      \"ty\": 4,\n      \"nm\": \"Слой 24\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.148,\n                \"y\": 0.956\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p148_0p956_0p333_0\",\n              \"t\": 0,\n              \"s\": [355, 531.333, 0],\n              \"e\": [355, 475.167, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.833,\n                \"y\": 0.833\n              },\n              \"o\": {\n                \"x\": 0.673,\n                \"y\": 0\n              },\n              \"n\": \"0p833_0p833_0p673_0\",\n              \"t\": 11,\n              \"s\": [355, 475.167, 0],\n              \"e\": [355, 684.167, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 24\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 176.333, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0, -22.091],\n                    [22.091, 0],\n                    [0, 22.091],\n                    [-22.091, 0]\n                  ],\n                  \"o\": [\n                    [0, 22.091],\n                    [-22.091, 0],\n                    [0, -22.091],\n                    [22.091, 0]\n                  ],\n                  \"v\": [\n                    [40, 176.333],\n                    [0, 216.333],\n                    [-40, 176.333],\n                    [0, 136.333]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 1,\n                \"ix\": 5\n              },\n              \"lc\": 1,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 3,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 25,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 10,\n      \"ty\": 4,\n      \"nm\": \"Слой 23\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [355, 355, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0, -22.091],\n                    [22.091, 0],\n                    [0, 22.091],\n                    [-22.091, 0]\n                  ],\n                  \"o\": [\n                    [0, 22.091],\n                    [-22.091, 0],\n                    [0, -22.091],\n                    [22.091, 0]\n                  ],\n                  \"v\": [\n                    [40, 0],\n                    [0, 40],\n                    [-40, 0],\n                    [0, -40]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 1,\n                \"ix\": 5\n              },\n              \"lc\": 1,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 3,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 156,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 11,\n      \"ty\": 4,\n      \"nm\": \"Слой 22\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.148,\n                \"y\": 0.964\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p148_0p964_0p333_0\",\n              \"t\": 0,\n              \"s\": [355, 178, 0],\n              \"e\": [355, 246.5, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.833,\n                \"y\": 0.833\n              },\n              \"o\": {\n                \"x\": 0.673,\n                \"y\": 0\n              },\n              \"n\": \"0p833_0p833_0p673_0\",\n              \"t\": 11,\n              \"s\": [355, 246.5, 0],\n              \"e\": [355, 54.5, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"t\": 24\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, -177, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [0, -22.091],\n                    [22.091, 0],\n                    [0, 22.091],\n                    [-22.091, 0]\n                  ],\n                  \"o\": [\n                    [0, 22.091],\n                    [-22.091, 0],\n                    [0, -22.091],\n                    [22.091, 0]\n                  ],\n                  \"v\": [\n                    [40, -177],\n                    [0, -137],\n                    [-40, -177],\n                    [0, -217]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 1,\n                \"ix\": 5\n              },\n              \"lc\": 1,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 3,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 25,\n      \"st\": 0,\n      \"bm\": 0\n    }\n  ],\n  \"markers\": []\n}\n"
  },
  {
    "path": "app/assets/lotties/lottieflow-social-networks-15-10-000000-easey.json",
    "content": "{\n  \"v\": \"5.3.4\",\n  \"fr\": 60,\n  \"ip\": 0,\n  \"op\": 120,\n  \"w\": 698,\n  \"h\": 756,\n  \"nm\": \"twit\",\n  \"ddd\": 0,\n  \"assets\": [],\n  \"layers\": [\n    {\n      \"ddd\": 0,\n      \"ind\": 1,\n      \"ty\": 4,\n      \"nm\": \"Слой 9\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.329,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p329_1_0p333_0\",\n              \"t\": 10,\n              \"s\": [336, 470, 0],\n              \"e\": [309, 499, 0],\n              \"to\": [3.5, 11.8333330154419, 0],\n              \"ti\": [18.8029289245605, 15.9662799835205, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.567,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p567_1_0p333_0\",\n              \"t\": 29,\n              \"s\": [309, 499, 0],\n              \"e\": [392, 333, 0],\n              \"to\": [-38.6666679382324, -32.8333320617676, 0],\n              \"ti\": [-42.5472412109375, -20.2980270385742, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.506,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p506_1_0p333_0\",\n              \"t\": 67,\n              \"s\": [392, 333, 0],\n              \"e\": [336, 470, 0],\n              \"to\": [49.8513221740723, 23.7825870513916, 0],\n              \"ti\": [9.33333301544189, -22.8333339691162, 0]\n            },\n            {\n              \"t\": 119\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 0.833\n                    },\n                    \"o\": {\n                      \"x\": 0.422,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_0p833_0p422_0\",\n                    \"t\": 3,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 162.673],\n                          [0.36, 5.28],\n                          [-16.527, 24.717],\n                          [24.403, -2.885],\n                          [-9.186, 28.5],\n                          [27.566, -5.436],\n                          [33.974, 0],\n                          [0, -67.594],\n                          [-2.119, -9.134],\n                          [62.477, 76.864],\n                          [-53.79, -35.918],\n                          [17.116, 9.543],\n                          [0, 0],\n                          [-57.22, -11.558],\n                          [18.443, 3.552],\n                          [-52.534, -0.987],\n                          [55.431, 0.055],\n                          [9.724, 1.165],\n                          [-66.884, 0.089]\n                        ],\n                        \"o\": [\n                          [226.416, 0],\n                          [0, -5.327],\n                          [24.091, -17.427],\n                          [-22.466, 9.955],\n                          [25.692, -15.38],\n                          [-24.163, 14.338],\n                          [-23.276, -24.751],\n                          [-67.596, 0],\n                          [0, 9.377],\n                          [-98.927, -4.958],\n                          [-32.489, 55.929],\n                          [-19.588, -0.58],\n                          [0, 0],\n                          [0.016, 58.376],\n                          [-18.122, 4.942],\n                          [16.086, 50.021],\n                          [-43.582, 34.25],\n                          [-9.795, -0.019],\n                          [56.29, 36.123],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-111.312, 243.765],\n                          [238.944, -106.491],\n                          [238.584, -122.402],\n                          [300, -186.122],\n                          [229.296, -166.754],\n                          [283.416, -234.843],\n                          [205.248, -204.962],\n                          [115.491, -243.765],\n                          [-7.726, -120.55],\n                          [-4.536, -92.691],\n                          [-258.24, -221.306],\n                          [-220.129, -56.979],\n                          [-276, -72.387],\n                          [-276, -70.827],\n                          [-177.24, 49.846],\n                          [-232.823, 51.957],\n                          [-117.816, 137.446],\n                          [-270.696, 190.246],\n                          [-300, 188.469],\n                          [-111.312, 243.669]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [12.653, 162.18],\n                          [0.769, 5.236],\n                          [-14.554, 25.928],\n                          [24.255, -9.341],\n                          [-15.295, 12.39],\n                          [27.06, -7.564],\n                          [33.871, -2.642],\n                          [-5.257, -67.389],\n                          [-2.823, -8.941],\n                          [96.444, 69.626],\n                          [-53.79, -35.918],\n                          [17.116, 9.543],\n                          [0, 0],\n                          [-57.22, -11.558],\n                          [17.027, -19.638],\n                          [-71.981, 57.874],\n                          [55.431, 0.055],\n                          [9.724, 1.165],\n                          [-66.884, 0.089]\n                        ],\n                        \"o\": [\n                          [226.416, 0],\n                          [-0.414, -5.311],\n                          [21.122, -26.501],\n                          [-18.613, 15.938],\n                          [26.63, -11.031],\n                          [-29.196, -5.082],\n                          [-25.13, -22.866],\n                          [-67.391, 5.258],\n                          [0.729, 9.349],\n                          [-102.685, 37.11],\n                          [-32.489, 55.929],\n                          [-19.588, -0.58],\n                          [0, 0],\n                          [0.016, 58.376],\n                          [-18.122, 4.942],\n                          [68.027, 42.362],\n                          [-73.981, 35.874],\n                          [-9.795, -0.019],\n                          [56.29, 36.123],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-111.312, 243.765],\n                          [237.494, -118.321],\n                          [235.774, -142.171],\n                          [274.102, -209.074],\n                          [223.188, -177.651],\n                          [274.001, -209.288],\n                          [196.241, -213.873],\n                          [103.738, -245.577],\n                          [-9.522, -113.151],\n                          [-4.175, -85.625],\n                          [-288.24, -163.306],\n                          [-250.129, 1.021],\n                          [-306, -14.387],\n                          [-306, -12.827],\n                          [-207.24, 107.846],\n                          [-289.823, 122.957],\n                          [-117.816, 137.446],\n                          [-270.696, 190.246],\n                          [-300, 188.469],\n                          [-111.312, 243.669]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 0.833\n                    },\n                    \"o\": {\n                      \"x\": 0.167,\n                      \"y\": 0.167\n                    },\n                    \"n\": \"0p833_0p833_0p167_0p167\",\n                    \"t\": 22,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [12.653, 162.18],\n                          [0.769, 5.236],\n                          [-14.554, 25.928],\n                          [24.255, -9.341],\n                          [-15.295, 12.39],\n                          [27.06, -7.564],\n                          [33.871, -2.642],\n                          [-5.257, -67.389],\n                          [-2.823, -8.941],\n                          [96.444, 69.626],\n                          [-53.79, -35.918],\n                          [17.116, 9.543],\n                          [0, 0],\n                          [-57.22, -11.558],\n                          [17.027, -19.638],\n                          [-71.981, 57.874],\n                          [55.431, 0.055],\n                          [9.724, 1.165],\n                          [-66.884, 0.089]\n                        ],\n                        \"o\": [\n                          [226.416, 0],\n                          [-0.414, -5.311],\n                          [21.122, -26.501],\n                          [-18.613, 15.938],\n                          [26.63, -11.031],\n                          [-29.196, -5.082],\n                          [-25.13, -22.866],\n                          [-67.391, 5.258],\n                          [0.729, 9.349],\n                          [-102.685, 37.11],\n                          [-32.489, 55.929],\n                          [-19.588, -0.58],\n                          [0, 0],\n                          [0.016, 58.376],\n                          [-18.122, 4.942],\n                          [68.027, 42.362],\n                          [-73.981, 35.874],\n                          [-9.795, -0.019],\n                          [56.29, 36.123],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-111.312, 243.765],\n                          [237.494, -118.321],\n                          [235.774, -142.171],\n                          [274.102, -209.074],\n                          [223.188, -177.651],\n                          [274.001, -209.288],\n                          [196.241, -213.873],\n                          [103.738, -245.577],\n                          [-9.522, -113.151],\n                          [-4.175, -85.625],\n                          [-288.24, -163.306],\n                          [-250.129, 1.021],\n                          [-306, -14.387],\n                          [-306, -12.827],\n                          [-207.24, 107.846],\n                          [-289.823, 122.957],\n                          [-117.816, 137.446],\n                          [-270.696, 190.246],\n                          [-300, 188.469],\n                          [-111.312, 243.669]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [30.024, 203.277],\n                          [0.36, 5.28],\n                          [-16.527, 24.717],\n                          [24.482, -3.552],\n                          [-19.158, 21.027],\n                          [27.566, -5.436],\n                          [33.974, 0],\n                          [-0.064, -67.594],\n                          [0.772, -0.118],\n                          [124.422, 51.371],\n                          [-55.784, -33.641],\n                          [17.909, 9.01],\n                          [0, 0],\n                          [-61.577, -11.845],\n                          [19.862, 3.662],\n                          [-40.638, -4.544],\n                          [70.504, -4.272],\n                          [12.308, -0.496],\n                          [-71.381, -0.81]\n                        ],\n                        \"o\": [\n                          [243.619, -21.906],\n                          [-0.778, -5.27],\n                          [24.091, -17.427],\n                          [-22.705, 9.133],\n                          [25.249, -10.834],\n                          [-22.482, 0.658],\n                          [-23.276, -24.751],\n                          [-67.596, 0],\n                          [0.034, 35.909],\n                          [-22.567, -53.779],\n                          [-40.204, 49.415],\n                          [-21.362, -1.021],\n                          [0, 0],\n                          [-4.878, 52.444],\n                          [-20.223, 3.977],\n                          [17.952, 68.372],\n                          [-40.284, 23.915],\n                          [-9.777, 0.592],\n                          [55.808, 40.004],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-105.368, 247.821],\n                          [238.888, -110.023],\n                          [238.528, -125.935],\n                          [293.047, -186.289],\n                          [229.24, -166.698],\n                          [283.808, -213.14],\n                          [205.192, -204.906],\n                          [115.434, -243.709],\n                          [-5.782, -108.494],\n                          [-6.021, -69.467],\n                          [-235.51, -242.117],\n                          [-231.634, -75.517],\n                          [-291.413, -90.786],\n                          [-291.544, -89.384],\n                          [-193.714, 21.547],\n                          [-254.648, 22.024],\n                          [-104.964, 168.501],\n                          [-229.752, 203.188],\n                          [-266.056, 204.911],\n                          [-105.368, 247.725]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 0.833\n                    },\n                    \"o\": {\n                      \"x\": 0.167,\n                      \"y\": 0.167\n                    },\n                    \"n\": \"0p833_0p833_0p167_0p167\",\n                    \"t\": 39,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [30.024, 203.277],\n                          [0.36, 5.28],\n                          [-16.527, 24.717],\n                          [24.482, -3.552],\n                          [-19.158, 21.027],\n                          [27.566, -5.436],\n                          [33.974, 0],\n                          [-0.064, -67.594],\n                          [0.772, -0.118],\n                          [124.422, 51.371],\n                          [-55.784, -33.641],\n                          [17.909, 9.01],\n                          [0, 0],\n                          [-61.577, -11.845],\n                          [19.862, 3.662],\n                          [-40.638, -4.544],\n                          [70.504, -4.272],\n                          [12.308, -0.496],\n                          [-71.381, -0.81]\n                        ],\n                        \"o\": [\n                          [243.619, -21.906],\n                          [-0.778, -5.27],\n                          [24.091, -17.427],\n                          [-22.705, 9.133],\n                          [25.249, -10.834],\n                          [-22.482, 0.658],\n                          [-23.276, -24.751],\n                          [-67.596, 0],\n                          [0.034, 35.909],\n                          [-22.567, -53.779],\n                          [-40.204, 49.415],\n                          [-21.362, -1.021],\n                          [0, 0],\n                          [-4.878, 52.444],\n                          [-20.223, 3.977],\n                          [17.952, 68.372],\n                          [-40.284, 23.915],\n                          [-9.777, 0.592],\n                          [55.808, 40.004],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-105.368, 247.821],\n                          [238.888, -110.023],\n                          [238.528, -125.935],\n                          [293.047, -186.289],\n                          [229.24, -166.698],\n                          [283.808, -213.14],\n                          [205.192, -204.906],\n                          [115.434, -243.709],\n                          [-5.782, -108.494],\n                          [-6.021, -69.467],\n                          [-235.51, -242.117],\n                          [-231.634, -75.517],\n                          [-291.413, -90.786],\n                          [-291.544, -89.384],\n                          [-193.714, 21.547],\n                          [-254.648, 22.024],\n                          [-104.964, 168.501],\n                          [-229.752, 203.188],\n                          [-266.056, 204.911],\n                          [-105.368, 247.725]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [29.427, 211.482],\n                          [0.36, 5.28],\n                          [-16.527, 24.717],\n                          [24.403, -2.885],\n                          [-9.186, 28.5],\n                          [27.566, -5.436],\n                          [33.974, 0],\n                          [0, -67.594],\n                          [4.137, -9.343],\n                          [2.067, 105.555],\n                          [-21.286, -64.16],\n                          [7.99, 18.75],\n                          [0, 0],\n                          [-39.823, -44.719],\n                          [12.947, 14.26],\n                          [-42.766, -32.558],\n                          [55.431, 0.055],\n                          [9.724, 1.165],\n                          [-66.884, 0.089]\n                        ],\n                        \"o\": [\n                          [226.416, 0],\n                          [-0.734, -5.276],\n                          [24.091, -17.427],\n                          [-22.466, 9.955],\n                          [25.692, -15.38],\n                          [-24.163, 14.338],\n                          [-23.276, -24.751],\n                          [-67.596, 0],\n                          [0, 9.377],\n                          [20.197, -76.835],\n                          [-62.896, 29.788],\n                          [-15.809, -12.327],\n                          [0, 0],\n                          [-37.618, 51.555],\n                          [-18.158, -6.567],\n                          [-13.452, 88.638],\n                          [-44.859, 27.567],\n                          [-9.795, -0.019],\n                          [56.29, 36.123],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-111.312, 243.765],\n                          [238.944, -106.491],\n                          [238.584, -122.402],\n                          [300, -186.122],\n                          [223.296, -176.754],\n                          [263.545, -244.972],\n                          [199.248, -217.962],\n                          [115.491, -243.765],\n                          [-7.726, -120.55],\n                          [47.303, -63.665],\n                          [-79.388, -330.257],\n                          [-153.834, -162.17],\n                          [-190.061, -209.475],\n                          [-191.067, -208.098],\n                          [-187.265, -41.976],\n                          [-234.548, -73.638],\n                          [-123.141, 153.433],\n                          [-270.696, 190.246],\n                          [-300, 188.469],\n                          [-111.312, 243.669]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 0.833\n                    },\n                    \"o\": {\n                      \"x\": 0.167,\n                      \"y\": 0.167\n                    },\n                    \"n\": \"0p833_0p833_0p167_0p167\",\n                    \"t\": 57,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [29.427, 211.482],\n                          [0.36, 5.28],\n                          [-16.527, 24.717],\n                          [24.403, -2.885],\n                          [-9.186, 28.5],\n                          [27.566, -5.436],\n                          [33.974, 0],\n                          [0, -67.594],\n                          [4.137, -9.343],\n                          [2.067, 105.555],\n                          [-21.286, -64.16],\n                          [7.99, 18.75],\n                          [0, 0],\n                          [-39.823, -44.719],\n                          [12.947, 14.26],\n                          [-42.766, -32.558],\n                          [55.431, 0.055],\n                          [9.724, 1.165],\n                          [-66.884, 0.089]\n                        ],\n                        \"o\": [\n                          [226.416, 0],\n                          [-0.734, -5.276],\n                          [24.091, -17.427],\n                          [-22.466, 9.955],\n                          [25.692, -15.38],\n                          [-24.163, 14.338],\n                          [-23.276, -24.751],\n                          [-67.596, 0],\n                          [0, 9.377],\n                          [20.197, -76.835],\n                          [-62.896, 29.788],\n                          [-15.809, -12.327],\n                          [0, 0],\n                          [-37.618, 51.555],\n                          [-18.158, -6.567],\n                          [-13.452, 88.638],\n                          [-44.859, 27.567],\n                          [-9.795, -0.019],\n                          [56.29, 36.123],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-111.312, 243.765],\n                          [238.944, -106.491],\n                          [238.584, -122.402],\n                          [300, -186.122],\n                          [223.296, -176.754],\n                          [263.545, -244.972],\n                          [199.248, -217.962],\n                          [115.491, -243.765],\n                          [-7.726, -120.55],\n                          [47.303, -63.665],\n                          [-79.388, -330.257],\n                          [-153.834, -162.17],\n                          [-190.061, -209.475],\n                          [-191.067, -208.098],\n                          [-187.265, -41.976],\n                          [-234.548, -73.638],\n                          [-123.141, 153.433],\n                          [-270.696, 190.246],\n                          [-300, 188.469],\n                          [-111.312, 243.669]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 162.673],\n                          [0.36, 5.28],\n                          [-16.527, 24.717],\n                          [24.403, -2.885],\n                          [-9.186, 28.5],\n                          [27.566, -5.436],\n                          [33.974, 0],\n                          [-2.007, -67.564],\n                          [0.073, 1.079],\n                          [87.387, 122.37],\n                          [-39.04, -48.734],\n                          [12.975, 13.721],\n                          [0, 0],\n                          [-49.325, -26.607],\n                          [15.949, 8.411],\n                          [-75.945, 26.843],\n                          [55.431, 0.055],\n                          [9.724, 1.165],\n                          [-66.884, 0.089]\n                        ],\n                        \"o\": [\n                          [226.416, 0],\n                          [0, -5.327],\n                          [24.091, -17.427],\n                          [-22.466, 9.955],\n                          [25.692, -15.38],\n                          [-24.163, 14.338],\n                          [-23.276, -24.751],\n                          [-67.596, 0],\n                          [3.542, 119.228],\n                          [-4.679, -69.151],\n                          [-46.288, 44.066],\n                          [-17.873, -5.911],\n                          [0, 0],\n                          [-17.062, 55.281],\n                          [-18.138, -0.281],\n                          [-22.571, 90.873],\n                          [-44.161, 31.218],\n                          [-9.795, -0.019],\n                          [56.29, 36.123],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-111.312, 243.765],\n                          [238.944, -106.491],\n                          [238.584, -122.402],\n                          [300, -186.122],\n                          [229.296, -166.754],\n                          [283.416, -234.843],\n                          [205.248, -204.962],\n                          [115.491, -243.765],\n                          [-7.726, -120.55],\n                          [18.989, -79.519],\n                          [-223.077, -277.04],\n                          [-236.044, -111.006],\n                          [-283.001, -140.889],\n                          [-283.457, -139.412],\n                          [-227.79, 1.885],\n                          [-279.606, -11.329],\n                          [-120.232, 144.701],\n                          [-270.696, 190.246],\n                          [-300, 188.469],\n                          [-111.312, 243.669]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.573,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.167,\n                      \"y\": 0.167\n                    },\n                    \"n\": \"0p573_1_0p167_0p167\",\n                    \"t\": 78,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 162.673],\n                          [0.36, 5.28],\n                          [-16.527, 24.717],\n                          [24.403, -2.885],\n                          [-9.186, 28.5],\n                          [27.566, -5.436],\n                          [33.974, 0],\n                          [-2.007, -67.564],\n                          [0.073, 1.079],\n                          [87.387, 122.37],\n                          [-39.04, -48.734],\n                          [12.975, 13.721],\n                          [0, 0],\n                          [-49.325, -26.607],\n                          [15.949, 8.411],\n                          [-75.945, 26.843],\n                          [55.431, 0.055],\n                          [9.724, 1.165],\n                          [-66.884, 0.089]\n                        ],\n                        \"o\": [\n                          [226.416, 0],\n                          [0, -5.327],\n                          [24.091, -17.427],\n                          [-22.466, 9.955],\n                          [25.692, -15.38],\n                          [-24.163, 14.338],\n                          [-23.276, -24.751],\n                          [-67.596, 0],\n                          [3.542, 119.228],\n                          [-4.679, -69.151],\n                          [-46.288, 44.066],\n                          [-17.873, -5.911],\n                          [0, 0],\n                          [-17.062, 55.281],\n                          [-18.138, -0.281],\n                          [-22.571, 90.873],\n                          [-44.161, 31.218],\n                          [-9.795, -0.019],\n                          [56.29, 36.123],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-111.312, 243.765],\n                          [238.944, -106.491],\n                          [238.584, -122.402],\n                          [300, -186.122],\n                          [229.296, -166.754],\n                          [283.416, -234.843],\n                          [205.248, -204.962],\n                          [115.491, -243.765],\n                          [-7.726, -120.55],\n                          [18.989, -79.519],\n                          [-223.077, -277.04],\n                          [-236.044, -111.006],\n                          [-283.001, -140.889],\n                          [-283.457, -139.412],\n                          [-227.79, 1.885],\n                          [-279.606, -11.329],\n                          [-120.232, 144.701],\n                          [-270.696, 190.246],\n                          [-300, 188.469],\n                          [-111.312, 243.669]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 162.673],\n                          [0.36, 5.28],\n                          [-16.527, 24.717],\n                          [24.403, -2.885],\n                          [-9.186, 28.5],\n                          [27.566, -5.436],\n                          [33.974, 0],\n                          [0, -67.594],\n                          [-2.119, -9.134],\n                          [62.477, 76.864],\n                          [-53.79, -35.918],\n                          [17.116, 9.543],\n                          [0, 0],\n                          [-57.22, -11.558],\n                          [18.443, 3.552],\n                          [-52.534, -0.987],\n                          [55.431, 0.055],\n                          [9.724, 1.165],\n                          [-66.884, 0.089]\n                        ],\n                        \"o\": [\n                          [226.416, 0],\n                          [0, -5.327],\n                          [24.091, -17.427],\n                          [-22.466, 9.955],\n                          [25.692, -15.38],\n                          [-24.163, 14.338],\n                          [-23.276, -24.751],\n                          [-67.596, 0],\n                          [0, 9.377],\n                          [-98.927, -4.958],\n                          [-32.489, 55.929],\n                          [-19.588, -0.58],\n                          [0, 0],\n                          [0.016, 58.376],\n                          [-18.122, 4.942],\n                          [16.086, 50.021],\n                          [-43.582, 34.25],\n                          [-9.795, -0.019],\n                          [56.29, 36.123],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-111.312, 243.765],\n                          [238.944, -106.491],\n                          [238.584, -122.402],\n                          [300, -186.122],\n                          [229.296, -166.754],\n                          [283.416, -234.843],\n                          [205.248, -204.962],\n                          [115.491, -243.765],\n                          [-7.726, -120.55],\n                          [-4.536, -92.691],\n                          [-258.24, -221.306],\n                          [-220.129, -56.979],\n                          [-276, -72.387],\n                          [-276, -70.827],\n                          [-177.24, 49.846],\n                          [-232.823, 51.957],\n                          [-117.816, 137.446],\n                          [-270.696, 190.246],\n                          [-300, 188.469],\n                          [-111.312, 243.669]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 110\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 120,\n      \"st\": 0,\n      \"bm\": 0\n    }\n  ],\n  \"markers\": []\n}\n"
  },
  {
    "path": "app/assets/lotties/lottieflow-social-networks-15-10-FFFFFF-easey.json",
    "content": "{\n  \"v\": \"5.3.4\",\n  \"fr\": 60,\n  \"ip\": 0,\n  \"op\": 120,\n  \"w\": 698,\n  \"h\": 756,\n  \"nm\": \"twit\",\n  \"ddd\": 0,\n  \"assets\": [],\n  \"layers\": [\n    {\n      \"ddd\": 0,\n      \"ind\": 1,\n      \"ty\": 4,\n      \"nm\": \"Слой 9\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.329,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p329_1_0p333_0\",\n              \"t\": 10,\n              \"s\": [336, 470, 0],\n              \"e\": [309, 499, 0],\n              \"to\": [3.5, 11.8333330154419, 0],\n              \"ti\": [18.8029289245605, 15.9662799835205, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.567,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p567_1_0p333_0\",\n              \"t\": 29,\n              \"s\": [309, 499, 0],\n              \"e\": [392, 333, 0],\n              \"to\": [-38.6666679382324, -32.8333320617676, 0],\n              \"ti\": [-42.5472412109375, -20.2980270385742, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.506,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p506_1_0p333_0\",\n              \"t\": 67,\n              \"s\": [392, 333, 0],\n              \"e\": [336, 470, 0],\n              \"to\": [49.8513221740723, 23.7825870513916, 0],\n              \"ti\": [9.33333301544189, -22.8333339691162, 0]\n            },\n            {\n              \"t\": 119\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 0.833\n                    },\n                    \"o\": {\n                      \"x\": 0.422,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_0p833_0p422_0\",\n                    \"t\": 3,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 162.673],\n                          [0.36, 5.28],\n                          [-16.527, 24.717],\n                          [24.403, -2.885],\n                          [-9.186, 28.5],\n                          [27.566, -5.436],\n                          [33.974, 0],\n                          [0, -67.594],\n                          [-2.119, -9.134],\n                          [62.477, 76.864],\n                          [-53.79, -35.918],\n                          [17.116, 9.543],\n                          [0, 0],\n                          [-57.22, -11.558],\n                          [18.443, 3.552],\n                          [-52.534, -0.987],\n                          [55.431, 0.055],\n                          [9.724, 1.165],\n                          [-66.884, 0.089]\n                        ],\n                        \"o\": [\n                          [226.416, 0],\n                          [0, -5.327],\n                          [24.091, -17.427],\n                          [-22.466, 9.955],\n                          [25.692, -15.38],\n                          [-24.163, 14.338],\n                          [-23.276, -24.751],\n                          [-67.596, 0],\n                          [0, 9.377],\n                          [-98.927, -4.958],\n                          [-32.489, 55.929],\n                          [-19.588, -0.58],\n                          [0, 0],\n                          [0.016, 58.376],\n                          [-18.122, 4.942],\n                          [16.086, 50.021],\n                          [-43.582, 34.25],\n                          [-9.795, -0.019],\n                          [56.29, 36.123],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-111.312, 243.765],\n                          [238.944, -106.491],\n                          [238.584, -122.402],\n                          [300, -186.122],\n                          [229.296, -166.754],\n                          [283.416, -234.843],\n                          [205.248, -204.962],\n                          [115.491, -243.765],\n                          [-7.726, -120.55],\n                          [-4.536, -92.691],\n                          [-258.24, -221.306],\n                          [-220.129, -56.979],\n                          [-276, -72.387],\n                          [-276, -70.827],\n                          [-177.24, 49.846],\n                          [-232.823, 51.957],\n                          [-117.816, 137.446],\n                          [-270.696, 190.246],\n                          [-300, 188.469],\n                          [-111.312, 243.669]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [12.653, 162.18],\n                          [0.769, 5.236],\n                          [-14.554, 25.928],\n                          [24.255, -9.341],\n                          [-15.295, 12.39],\n                          [27.06, -7.564],\n                          [33.871, -2.642],\n                          [-5.257, -67.389],\n                          [-2.823, -8.941],\n                          [96.444, 69.626],\n                          [-53.79, -35.918],\n                          [17.116, 9.543],\n                          [0, 0],\n                          [-57.22, -11.558],\n                          [17.027, -19.638],\n                          [-71.981, 57.874],\n                          [55.431, 0.055],\n                          [9.724, 1.165],\n                          [-66.884, 0.089]\n                        ],\n                        \"o\": [\n                          [226.416, 0],\n                          [-0.414, -5.311],\n                          [21.122, -26.501],\n                          [-18.613, 15.938],\n                          [26.63, -11.031],\n                          [-29.196, -5.082],\n                          [-25.13, -22.866],\n                          [-67.391, 5.258],\n                          [0.729, 9.349],\n                          [-102.685, 37.11],\n                          [-32.489, 55.929],\n                          [-19.588, -0.58],\n                          [0, 0],\n                          [0.016, 58.376],\n                          [-18.122, 4.942],\n                          [68.027, 42.362],\n                          [-73.981, 35.874],\n                          [-9.795, -0.019],\n                          [56.29, 36.123],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-111.312, 243.765],\n                          [237.494, -118.321],\n                          [235.774, -142.171],\n                          [274.102, -209.074],\n                          [223.188, -177.651],\n                          [274.001, -209.288],\n                          [196.241, -213.873],\n                          [103.738, -245.577],\n                          [-9.522, -113.151],\n                          [-4.175, -85.625],\n                          [-288.24, -163.306],\n                          [-250.129, 1.021],\n                          [-306, -14.387],\n                          [-306, -12.827],\n                          [-207.24, 107.846],\n                          [-289.823, 122.957],\n                          [-117.816, 137.446],\n                          [-270.696, 190.246],\n                          [-300, 188.469],\n                          [-111.312, 243.669]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 0.833\n                    },\n                    \"o\": {\n                      \"x\": 0.167,\n                      \"y\": 0.167\n                    },\n                    \"n\": \"0p833_0p833_0p167_0p167\",\n                    \"t\": 22,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [12.653, 162.18],\n                          [0.769, 5.236],\n                          [-14.554, 25.928],\n                          [24.255, -9.341],\n                          [-15.295, 12.39],\n                          [27.06, -7.564],\n                          [33.871, -2.642],\n                          [-5.257, -67.389],\n                          [-2.823, -8.941],\n                          [96.444, 69.626],\n                          [-53.79, -35.918],\n                          [17.116, 9.543],\n                          [0, 0],\n                          [-57.22, -11.558],\n                          [17.027, -19.638],\n                          [-71.981, 57.874],\n                          [55.431, 0.055],\n                          [9.724, 1.165],\n                          [-66.884, 0.089]\n                        ],\n                        \"o\": [\n                          [226.416, 0],\n                          [-0.414, -5.311],\n                          [21.122, -26.501],\n                          [-18.613, 15.938],\n                          [26.63, -11.031],\n                          [-29.196, -5.082],\n                          [-25.13, -22.866],\n                          [-67.391, 5.258],\n                          [0.729, 9.349],\n                          [-102.685, 37.11],\n                          [-32.489, 55.929],\n                          [-19.588, -0.58],\n                          [0, 0],\n                          [0.016, 58.376],\n                          [-18.122, 4.942],\n                          [68.027, 42.362],\n                          [-73.981, 35.874],\n                          [-9.795, -0.019],\n                          [56.29, 36.123],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-111.312, 243.765],\n                          [237.494, -118.321],\n                          [235.774, -142.171],\n                          [274.102, -209.074],\n                          [223.188, -177.651],\n                          [274.001, -209.288],\n                          [196.241, -213.873],\n                          [103.738, -245.577],\n                          [-9.522, -113.151],\n                          [-4.175, -85.625],\n                          [-288.24, -163.306],\n                          [-250.129, 1.021],\n                          [-306, -14.387],\n                          [-306, -12.827],\n                          [-207.24, 107.846],\n                          [-289.823, 122.957],\n                          [-117.816, 137.446],\n                          [-270.696, 190.246],\n                          [-300, 188.469],\n                          [-111.312, 243.669]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [30.024, 203.277],\n                          [0.36, 5.28],\n                          [-16.527, 24.717],\n                          [24.482, -3.552],\n                          [-19.158, 21.027],\n                          [27.566, -5.436],\n                          [33.974, 0],\n                          [-0.064, -67.594],\n                          [0.772, -0.118],\n                          [124.422, 51.371],\n                          [-55.784, -33.641],\n                          [17.909, 9.01],\n                          [0, 0],\n                          [-61.577, -11.845],\n                          [19.862, 3.662],\n                          [-40.638, -4.544],\n                          [70.504, -4.272],\n                          [12.308, -0.496],\n                          [-71.381, -0.81]\n                        ],\n                        \"o\": [\n                          [243.619, -21.906],\n                          [-0.778, -5.27],\n                          [24.091, -17.427],\n                          [-22.705, 9.133],\n                          [25.249, -10.834],\n                          [-22.482, 0.658],\n                          [-23.276, -24.751],\n                          [-67.596, 0],\n                          [0.034, 35.909],\n                          [-22.567, -53.779],\n                          [-40.204, 49.415],\n                          [-21.362, -1.021],\n                          [0, 0],\n                          [-4.878, 52.444],\n                          [-20.223, 3.977],\n                          [17.952, 68.372],\n                          [-40.284, 23.915],\n                          [-9.777, 0.592],\n                          [55.808, 40.004],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-105.368, 247.821],\n                          [238.888, -110.023],\n                          [238.528, -125.935],\n                          [293.047, -186.289],\n                          [229.24, -166.698],\n                          [283.808, -213.14],\n                          [205.192, -204.906],\n                          [115.434, -243.709],\n                          [-5.782, -108.494],\n                          [-6.021, -69.467],\n                          [-235.51, -242.117],\n                          [-231.634, -75.517],\n                          [-291.413, -90.786],\n                          [-291.544, -89.384],\n                          [-193.714, 21.547],\n                          [-254.648, 22.024],\n                          [-104.964, 168.501],\n                          [-229.752, 203.188],\n                          [-266.056, 204.911],\n                          [-105.368, 247.725]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 0.833\n                    },\n                    \"o\": {\n                      \"x\": 0.167,\n                      \"y\": 0.167\n                    },\n                    \"n\": \"0p833_0p833_0p167_0p167\",\n                    \"t\": 39,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [30.024, 203.277],\n                          [0.36, 5.28],\n                          [-16.527, 24.717],\n                          [24.482, -3.552],\n                          [-19.158, 21.027],\n                          [27.566, -5.436],\n                          [33.974, 0],\n                          [-0.064, -67.594],\n                          [0.772, -0.118],\n                          [124.422, 51.371],\n                          [-55.784, -33.641],\n                          [17.909, 9.01],\n                          [0, 0],\n                          [-61.577, -11.845],\n                          [19.862, 3.662],\n                          [-40.638, -4.544],\n                          [70.504, -4.272],\n                          [12.308, -0.496],\n                          [-71.381, -0.81]\n                        ],\n                        \"o\": [\n                          [243.619, -21.906],\n                          [-0.778, -5.27],\n                          [24.091, -17.427],\n                          [-22.705, 9.133],\n                          [25.249, -10.834],\n                          [-22.482, 0.658],\n                          [-23.276, -24.751],\n                          [-67.596, 0],\n                          [0.034, 35.909],\n                          [-22.567, -53.779],\n                          [-40.204, 49.415],\n                          [-21.362, -1.021],\n                          [0, 0],\n                          [-4.878, 52.444],\n                          [-20.223, 3.977],\n                          [17.952, 68.372],\n                          [-40.284, 23.915],\n                          [-9.777, 0.592],\n                          [55.808, 40.004],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-105.368, 247.821],\n                          [238.888, -110.023],\n                          [238.528, -125.935],\n                          [293.047, -186.289],\n                          [229.24, -166.698],\n                          [283.808, -213.14],\n                          [205.192, -204.906],\n                          [115.434, -243.709],\n                          [-5.782, -108.494],\n                          [-6.021, -69.467],\n                          [-235.51, -242.117],\n                          [-231.634, -75.517],\n                          [-291.413, -90.786],\n                          [-291.544, -89.384],\n                          [-193.714, 21.547],\n                          [-254.648, 22.024],\n                          [-104.964, 168.501],\n                          [-229.752, 203.188],\n                          [-266.056, 204.911],\n                          [-105.368, 247.725]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [29.427, 211.482],\n                          [0.36, 5.28],\n                          [-16.527, 24.717],\n                          [24.403, -2.885],\n                          [-9.186, 28.5],\n                          [27.566, -5.436],\n                          [33.974, 0],\n                          [0, -67.594],\n                          [4.137, -9.343],\n                          [2.067, 105.555],\n                          [-21.286, -64.16],\n                          [7.99, 18.75],\n                          [0, 0],\n                          [-39.823, -44.719],\n                          [12.947, 14.26],\n                          [-42.766, -32.558],\n                          [55.431, 0.055],\n                          [9.724, 1.165],\n                          [-66.884, 0.089]\n                        ],\n                        \"o\": [\n                          [226.416, 0],\n                          [-0.734, -5.276],\n                          [24.091, -17.427],\n                          [-22.466, 9.955],\n                          [25.692, -15.38],\n                          [-24.163, 14.338],\n                          [-23.276, -24.751],\n                          [-67.596, 0],\n                          [0, 9.377],\n                          [20.197, -76.835],\n                          [-62.896, 29.788],\n                          [-15.809, -12.327],\n                          [0, 0],\n                          [-37.618, 51.555],\n                          [-18.158, -6.567],\n                          [-13.452, 88.638],\n                          [-44.859, 27.567],\n                          [-9.795, -0.019],\n                          [56.29, 36.123],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-111.312, 243.765],\n                          [238.944, -106.491],\n                          [238.584, -122.402],\n                          [300, -186.122],\n                          [223.296, -176.754],\n                          [263.545, -244.972],\n                          [199.248, -217.962],\n                          [115.491, -243.765],\n                          [-7.726, -120.55],\n                          [47.303, -63.665],\n                          [-79.388, -330.257],\n                          [-153.834, -162.17],\n                          [-190.061, -209.475],\n                          [-191.067, -208.098],\n                          [-187.265, -41.976],\n                          [-234.548, -73.638],\n                          [-123.141, 153.433],\n                          [-270.696, 190.246],\n                          [-300, 188.469],\n                          [-111.312, 243.669]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 0.833\n                    },\n                    \"o\": {\n                      \"x\": 0.167,\n                      \"y\": 0.167\n                    },\n                    \"n\": \"0p833_0p833_0p167_0p167\",\n                    \"t\": 57,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [29.427, 211.482],\n                          [0.36, 5.28],\n                          [-16.527, 24.717],\n                          [24.403, -2.885],\n                          [-9.186, 28.5],\n                          [27.566, -5.436],\n                          [33.974, 0],\n                          [0, -67.594],\n                          [4.137, -9.343],\n                          [2.067, 105.555],\n                          [-21.286, -64.16],\n                          [7.99, 18.75],\n                          [0, 0],\n                          [-39.823, -44.719],\n                          [12.947, 14.26],\n                          [-42.766, -32.558],\n                          [55.431, 0.055],\n                          [9.724, 1.165],\n                          [-66.884, 0.089]\n                        ],\n                        \"o\": [\n                          [226.416, 0],\n                          [-0.734, -5.276],\n                          [24.091, -17.427],\n                          [-22.466, 9.955],\n                          [25.692, -15.38],\n                          [-24.163, 14.338],\n                          [-23.276, -24.751],\n                          [-67.596, 0],\n                          [0, 9.377],\n                          [20.197, -76.835],\n                          [-62.896, 29.788],\n                          [-15.809, -12.327],\n                          [0, 0],\n                          [-37.618, 51.555],\n                          [-18.158, -6.567],\n                          [-13.452, 88.638],\n                          [-44.859, 27.567],\n                          [-9.795, -0.019],\n                          [56.29, 36.123],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-111.312, 243.765],\n                          [238.944, -106.491],\n                          [238.584, -122.402],\n                          [300, -186.122],\n                          [223.296, -176.754],\n                          [263.545, -244.972],\n                          [199.248, -217.962],\n                          [115.491, -243.765],\n                          [-7.726, -120.55],\n                          [47.303, -63.665],\n                          [-79.388, -330.257],\n                          [-153.834, -162.17],\n                          [-190.061, -209.475],\n                          [-191.067, -208.098],\n                          [-187.265, -41.976],\n                          [-234.548, -73.638],\n                          [-123.141, 153.433],\n                          [-270.696, 190.246],\n                          [-300, 188.469],\n                          [-111.312, 243.669]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 162.673],\n                          [0.36, 5.28],\n                          [-16.527, 24.717],\n                          [24.403, -2.885],\n                          [-9.186, 28.5],\n                          [27.566, -5.436],\n                          [33.974, 0],\n                          [-2.007, -67.564],\n                          [0.073, 1.079],\n                          [87.387, 122.37],\n                          [-39.04, -48.734],\n                          [12.975, 13.721],\n                          [0, 0],\n                          [-49.325, -26.607],\n                          [15.949, 8.411],\n                          [-75.945, 26.843],\n                          [55.431, 0.055],\n                          [9.724, 1.165],\n                          [-66.884, 0.089]\n                        ],\n                        \"o\": [\n                          [226.416, 0],\n                          [0, -5.327],\n                          [24.091, -17.427],\n                          [-22.466, 9.955],\n                          [25.692, -15.38],\n                          [-24.163, 14.338],\n                          [-23.276, -24.751],\n                          [-67.596, 0],\n                          [3.542, 119.228],\n                          [-4.679, -69.151],\n                          [-46.288, 44.066],\n                          [-17.873, -5.911],\n                          [0, 0],\n                          [-17.062, 55.281],\n                          [-18.138, -0.281],\n                          [-22.571, 90.873],\n                          [-44.161, 31.218],\n                          [-9.795, -0.019],\n                          [56.29, 36.123],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-111.312, 243.765],\n                          [238.944, -106.491],\n                          [238.584, -122.402],\n                          [300, -186.122],\n                          [229.296, -166.754],\n                          [283.416, -234.843],\n                          [205.248, -204.962],\n                          [115.491, -243.765],\n                          [-7.726, -120.55],\n                          [18.989, -79.519],\n                          [-223.077, -277.04],\n                          [-236.044, -111.006],\n                          [-283.001, -140.889],\n                          [-283.457, -139.412],\n                          [-227.79, 1.885],\n                          [-279.606, -11.329],\n                          [-120.232, 144.701],\n                          [-270.696, 190.246],\n                          [-300, 188.469],\n                          [-111.312, 243.669]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.573,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.167,\n                      \"y\": 0.167\n                    },\n                    \"n\": \"0p573_1_0p167_0p167\",\n                    \"t\": 78,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 162.673],\n                          [0.36, 5.28],\n                          [-16.527, 24.717],\n                          [24.403, -2.885],\n                          [-9.186, 28.5],\n                          [27.566, -5.436],\n                          [33.974, 0],\n                          [-2.007, -67.564],\n                          [0.073, 1.079],\n                          [87.387, 122.37],\n                          [-39.04, -48.734],\n                          [12.975, 13.721],\n                          [0, 0],\n                          [-49.325, -26.607],\n                          [15.949, 8.411],\n                          [-75.945, 26.843],\n                          [55.431, 0.055],\n                          [9.724, 1.165],\n                          [-66.884, 0.089]\n                        ],\n                        \"o\": [\n                          [226.416, 0],\n                          [0, -5.327],\n                          [24.091, -17.427],\n                          [-22.466, 9.955],\n                          [25.692, -15.38],\n                          [-24.163, 14.338],\n                          [-23.276, -24.751],\n                          [-67.596, 0],\n                          [3.542, 119.228],\n                          [-4.679, -69.151],\n                          [-46.288, 44.066],\n                          [-17.873, -5.911],\n                          [0, 0],\n                          [-17.062, 55.281],\n                          [-18.138, -0.281],\n                          [-22.571, 90.873],\n                          [-44.161, 31.218],\n                          [-9.795, -0.019],\n                          [56.29, 36.123],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-111.312, 243.765],\n                          [238.944, -106.491],\n                          [238.584, -122.402],\n                          [300, -186.122],\n                          [229.296, -166.754],\n                          [283.416, -234.843],\n                          [205.248, -204.962],\n                          [115.491, -243.765],\n                          [-7.726, -120.55],\n                          [18.989, -79.519],\n                          [-223.077, -277.04],\n                          [-236.044, -111.006],\n                          [-283.001, -140.889],\n                          [-283.457, -139.412],\n                          [-227.79, 1.885],\n                          [-279.606, -11.329],\n                          [-120.232, 144.701],\n                          [-270.696, 190.246],\n                          [-300, 188.469],\n                          [-111.312, 243.669]\n                        ],\n                        \"c\": false\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, 0],\n                          [0, 162.673],\n                          [0.36, 5.28],\n                          [-16.527, 24.717],\n                          [24.403, -2.885],\n                          [-9.186, 28.5],\n                          [27.566, -5.436],\n                          [33.974, 0],\n                          [0, -67.594],\n                          [-2.119, -9.134],\n                          [62.477, 76.864],\n                          [-53.79, -35.918],\n                          [17.116, 9.543],\n                          [0, 0],\n                          [-57.22, -11.558],\n                          [18.443, 3.552],\n                          [-52.534, -0.987],\n                          [55.431, 0.055],\n                          [9.724, 1.165],\n                          [-66.884, 0.089]\n                        ],\n                        \"o\": [\n                          [226.416, 0],\n                          [0, -5.327],\n                          [24.091, -17.427],\n                          [-22.466, 9.955],\n                          [25.692, -15.38],\n                          [-24.163, 14.338],\n                          [-23.276, -24.751],\n                          [-67.596, 0],\n                          [0, 9.377],\n                          [-98.927, -4.958],\n                          [-32.489, 55.929],\n                          [-19.588, -0.58],\n                          [0, 0],\n                          [0.016, 58.376],\n                          [-18.122, 4.942],\n                          [16.086, 50.021],\n                          [-43.582, 34.25],\n                          [-9.795, -0.019],\n                          [56.29, 36.123],\n                          [0, 0]\n                        ],\n                        \"v\": [\n                          [-111.312, 243.765],\n                          [238.944, -106.491],\n                          [238.584, -122.402],\n                          [300, -186.122],\n                          [229.296, -166.754],\n                          [283.416, -234.843],\n                          [205.248, -204.962],\n                          [115.491, -243.765],\n                          [-7.726, -120.55],\n                          [-4.536, -92.691],\n                          [-258.24, -221.306],\n                          [-220.129, -56.979],\n                          [-276, -72.387],\n                          [-276, -70.827],\n                          [-177.24, 49.846],\n                          [-232.823, 51.957],\n                          [-117.816, 137.446],\n                          [-270.696, 190.246],\n                          [-300, 188.469],\n                          [-111.312, 243.669]\n                        ],\n                        \"c\": false\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 110\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 120,\n      \"st\": 0,\n      \"bm\": 0\n    }\n  ],\n  \"markers\": []\n}\n"
  },
  {
    "path": "app/assets/lotties/lottieflow-social-networks-15-4-000000-easey.json",
    "content": "{\n  \"v\": \"5.3.4\",\n  \"fr\": 60,\n  \"ip\": 0,\n  \"op\": 89,\n  \"w\": 654,\n  \"h\": 634,\n  \"nm\": \"F2\",\n  \"ddd\": 0,\n  \"assets\": [\n    {\n      \"id\": \"comp_0\",\n      \"layers\": [\n        {\n          \"ddd\": 0,\n          \"ind\": 1,\n          \"ty\": 4,\n          \"nm\": \"Слой 5\",\n          \"sr\": 1,\n          \"ks\": {\n            \"o\": {\n              \"a\": 0,\n              \"k\": 100,\n              \"ix\": 11\n            },\n            \"r\": {\n              \"a\": 0,\n              \"k\": 0,\n              \"ix\": 10\n            },\n            \"p\": {\n              \"a\": 0,\n              \"k\": [500, 500, 0],\n              \"ix\": 2\n            },\n            \"a\": {\n              \"a\": 0,\n              \"k\": [0, 0, 0],\n              \"ix\": 1\n            },\n            \"s\": {\n              \"a\": 0,\n              \"k\": [100, 100, 100],\n              \"ix\": 6\n            }\n          },\n          \"ao\": 0,\n          \"shapes\": [\n            {\n              \"ty\": \"gr\",\n              \"it\": [\n                {\n                  \"ind\": 0,\n                  \"ty\": \"sh\",\n                  \"ix\": 1,\n                  \"ks\": {\n                    \"a\": 1,\n                    \"k\": [\n                      {\n                        \"i\": {\n                          \"x\": 0.254,\n                          \"y\": 0.998\n                        },\n                        \"o\": {\n                          \"x\": 0.333,\n                          \"y\": 0\n                        },\n                        \"n\": \"0p254_0p998_0p333_0\",\n                        \"t\": 3,\n                        \"s\": [\n                          {\n                            \"i\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-37.267, 0],\n                              [0, 0],\n                              [0, 0],\n                              [32.823, 0],\n                              [0, -75.188],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-15.948, 0],\n                              [-15.275, 2.397],\n                              [0, 0]\n                            ],\n                            \"o\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, -23.725],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-68.527, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [15.275, 2.397],\n                              [15.948, 0],\n                              [0, 0],\n                              [0, 0]\n                            ],\n                            \"v\": [\n                              [116.777, 88.541],\n                              [130.078, 1.822],\n                              [46.875, 1.822],\n                              [46.875, -54.454],\n                              [95.764, -101.303],\n                              [133.594, -101.303],\n                              [133.594, -175.131],\n                              [66.439, -180.99],\n                              [-46.875, -64.271],\n                              [-46.875, 1.822],\n                              [-123.047, 1.822],\n                              [-123.047, 88.541],\n                              [-46.875, 88.541],\n                              [-46.875, 296.346],\n                              [0, 300],\n                              [46.875, 296.346],\n                              [46.875, 88.541]\n                            ],\n                            \"c\": true\n                          }\n                        ],\n                        \"e\": [\n                          {\n                            \"i\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-21.671, 0],\n                              [0, 0],\n                              [0, 0],\n                              [19.087, 0],\n                              [0, -43.723],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-9.274, 0],\n                              [-8.882, 1.394],\n                              [0, 0]\n                            ],\n                            \"o\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, -13.796],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-39.85, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [8.882, 1.394],\n                              [9.274, 0],\n                              [0, 0],\n                              [0, 0]\n                            ],\n                            \"v\": [\n                              [69.173, 147.374],\n                              [76.908, 96.945],\n                              [28.523, 96.945],\n                              [28.523, 64.219],\n                              [56.953, 36.976],\n                              [78.952, 36.976],\n                              [78.952, -5.957],\n                              [39.9, -9.364],\n                              [-25.994, 58.51],\n                              [-25.994, 96.945],\n                              [-70.29, 96.945],\n                              [-70.29, 147.374],\n                              [-25.994, 147.374],\n                              [-25.994, 268.216],\n                              [1.265, 270.341],\n                              [28.523, 268.216],\n                              [28.523, 147.374]\n                            ],\n                            \"c\": true\n                          }\n                        ]\n                      },\n                      {\n                        \"i\": {\n                          \"x\": 0.323,\n                          \"y\": 0.998\n                        },\n                        \"o\": {\n                          \"x\": 0.333,\n                          \"y\": 0\n                        },\n                        \"n\": \"0p323_0p998_0p333_0\",\n                        \"t\": 18,\n                        \"s\": [\n                          {\n                            \"i\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-21.671, 0],\n                              [0, 0],\n                              [0, 0],\n                              [19.087, 0],\n                              [0, -43.723],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-9.274, 0],\n                              [-8.882, 1.394],\n                              [0, 0]\n                            ],\n                            \"o\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, -13.796],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-39.85, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [8.882, 1.394],\n                              [9.274, 0],\n                              [0, 0],\n                              [0, 0]\n                            ],\n                            \"v\": [\n                              [69.173, 147.374],\n                              [76.908, 96.945],\n                              [28.523, 96.945],\n                              [28.523, 64.219],\n                              [56.953, 36.976],\n                              [78.952, 36.976],\n                              [78.952, -5.957],\n                              [39.9, -9.364],\n                              [-25.994, 58.51],\n                              [-25.994, 96.945],\n                              [-70.29, 96.945],\n                              [-70.29, 147.374],\n                              [-25.994, 147.374],\n                              [-25.994, 268.216],\n                              [1.265, 270.341],\n                              [28.523, 268.216],\n                              [28.523, 147.374]\n                            ],\n                            \"c\": true\n                          }\n                        ],\n                        \"e\": [\n                          {\n                            \"i\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-33.166, 0],\n                              [0, 0],\n                              [0, 0],\n                              [29.211, 0],\n                              [0, -66.914],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-14.193, 0],\n                              [-13.594, 2.133],\n                              [0, 0]\n                            ],\n                            \"o\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, -21.114],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-60.986, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [13.594, 2.133],\n                              [14.193, 0],\n                              [0, 0],\n                              [0, 0]\n                            ],\n                            \"v\": [\n                              [103.566, 73.814],\n                              [115.403, -3.363],\n                              [41.355, -3.363],\n                              [41.355, -53.447],\n                              [84.865, -95.141],\n                              [118.532, -95.141],\n                              [118.532, -160.845],\n                              [58.767, -166.06],\n                              [-42.079, -62.184],\n                              [-42.079, -3.363],\n                              [-109.87, -3.363],\n                              [-109.87, 73.814],\n                              [-42.079, 73.814],\n                              [-42.079, 258.754],\n                              [-0.362, 262.006],\n                              [41.355, 258.754],\n                              [41.355, 73.814]\n                            ],\n                            \"c\": true\n                          }\n                        ]\n                      },\n                      {\n                        \"i\": {\n                          \"x\": 0.398,\n                          \"y\": 0.998\n                        },\n                        \"o\": {\n                          \"x\": 0.333,\n                          \"y\": 0\n                        },\n                        \"n\": \"0p398_0p998_0p333_0\",\n                        \"t\": 36,\n                        \"s\": [\n                          {\n                            \"i\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-33.166, 0],\n                              [0, 0],\n                              [0, 0],\n                              [29.211, 0],\n                              [0, -66.914],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-14.193, 0],\n                              [-13.594, 2.133],\n                              [0, 0]\n                            ],\n                            \"o\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, -21.114],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-60.986, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [13.594, 2.133],\n                              [14.193, 0],\n                              [0, 0],\n                              [0, 0]\n                            ],\n                            \"v\": [\n                              [103.566, 73.814],\n                              [115.403, -3.363],\n                              [41.355, -3.363],\n                              [41.355, -53.447],\n                              [84.865, -95.141],\n                              [118.532, -95.141],\n                              [118.532, -160.845],\n                              [58.767, -166.06],\n                              [-42.079, -62.184],\n                              [-42.079, -3.363],\n                              [-109.87, -3.363],\n                              [-109.87, 73.814],\n                              [-42.079, 73.814],\n                              [-42.079, 258.754],\n                              [-0.362, 262.006],\n                              [41.355, 258.754],\n                              [41.355, 73.814]\n                            ],\n                            \"c\": true\n                          }\n                        ],\n                        \"e\": [\n                          {\n                            \"i\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-30.303, 0],\n                              [0, 0],\n                              [0, 0],\n                              [26.689, 0],\n                              [0, -61.137],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-12.967, 0],\n                              [-12.42, 1.949],\n                              [0, 0]\n                            ],\n                            \"o\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, -19.291],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-55.721, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [12.42, 1.949],\n                              [12.967, 0],\n                              [0, 0],\n                              [0, 0]\n                            ],\n                            \"v\": [\n                              [95.94, 109.041],\n                              [106.756, 38.528],\n                              [39.101, 38.528],\n                              [39.101, -7.232],\n                              [78.854, -45.326],\n                              [109.614, -45.326],\n                              [109.614, -105.358],\n                              [55.009, -110.122],\n                              [-37.13, -15.215],\n                              [-37.13, 38.528],\n                              [-99.067, 38.528],\n                              [-99.067, 109.041],\n                              [-37.13, 109.041],\n                              [-37.13, 278.014],\n                              [0.985, 280.984],\n                              [39.101, 278.014],\n                              [39.101, 109.041]\n                            ],\n                            \"c\": true\n                          }\n                        ]\n                      },\n                      {\n                        \"i\": {\n                          \"x\": 0.357,\n                          \"y\": 0.998\n                        },\n                        \"o\": {\n                          \"x\": 0.333,\n                          \"y\": 0\n                        },\n                        \"n\": \"0p357_0p998_0p333_0\",\n                        \"t\": 62,\n                        \"s\": [\n                          {\n                            \"i\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-30.303, 0],\n                              [0, 0],\n                              [0, 0],\n                              [26.689, 0],\n                              [0, -61.137],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-12.967, 0],\n                              [-12.42, 1.949],\n                              [0, 0]\n                            ],\n                            \"o\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, -19.291],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-55.721, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [12.42, 1.949],\n                              [12.967, 0],\n                              [0, 0],\n                              [0, 0]\n                            ],\n                            \"v\": [\n                              [95.94, 109.041],\n                              [106.756, 38.528],\n                              [39.101, 38.528],\n                              [39.101, -7.232],\n                              [78.854, -45.326],\n                              [109.614, -45.326],\n                              [109.614, -105.358],\n                              [55.009, -110.122],\n                              [-37.13, -15.215],\n                              [-37.13, 38.528],\n                              [-99.067, 38.528],\n                              [-99.067, 109.041],\n                              [-37.13, 109.041],\n                              [-37.13, 278.014],\n                              [0.985, 280.984],\n                              [39.101, 278.014],\n                              [39.101, 109.041]\n                            ],\n                            \"c\": true\n                          }\n                        ],\n                        \"e\": [\n                          {\n                            \"i\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-37.267, 0],\n                              [0, 0],\n                              [0, 0],\n                              [32.823, 0],\n                              [0, -75.188],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-15.948, 0],\n                              [-15.275, 2.397],\n                              [0, 0]\n                            ],\n                            \"o\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, -23.725],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-68.527, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [15.275, 2.397],\n                              [15.948, 0],\n                              [0, 0],\n                              [0, 0]\n                            ],\n                            \"v\": [\n                              [116.777, 88.541],\n                              [130.078, 1.822],\n                              [46.875, 1.822],\n                              [46.875, -54.454],\n                              [95.764, -101.303],\n                              [133.594, -101.303],\n                              [133.594, -175.131],\n                              [66.439, -180.99],\n                              [-46.875, -64.271],\n                              [-46.875, 1.822],\n                              [-123.047, 1.822],\n                              [-123.047, 88.541],\n                              [-46.875, 88.541],\n                              [-46.875, 296.346],\n                              [0, 300],\n                              [46.875, 296.346],\n                              [46.875, 88.541]\n                            ],\n                            \"c\": true\n                          }\n                        ]\n                      },\n                      {\n                        \"t\": 88\n                      }\n                    ],\n                    \"ix\": 2\n                  },\n                  \"nm\": \"Path 1\",\n                  \"mn\": \"ADBE Vector Shape - Group\",\n                  \"hd\": false\n                },\n                {\n                  \"ty\": \"st\",\n                  \"c\": {\n                    \"a\": 0,\n                    \"k\": [0, 0, 0, 1],\n                    \"ix\": 3\n                  },\n                  \"o\": {\n                    \"a\": 0,\n                    \"k\": 100,\n                    \"ix\": 4\n                  },\n                  \"w\": {\n                    \"a\": 0,\n                    \"k\": 1,\n                    \"ix\": 5\n                  },\n                  \"lc\": 1,\n                  \"lj\": 1,\n                  \"ml\": 10,\n                  \"ml2\": {\n                    \"a\": 0,\n                    \"k\": 10,\n                    \"ix\": 8\n                  },\n                  \"nm\": \"Stroke 1\",\n                  \"mn\": \"ADBE Vector Graphic - Stroke\",\n                  \"hd\": false\n                },\n                {\n                  \"ty\": \"fl\",\n                  \"c\": {\n                    \"a\": 0,\n                    \"k\": [1, 1, 1, 1],\n                    \"ix\": 4\n                  },\n                  \"o\": {\n                    \"a\": 0,\n                    \"k\": 100,\n                    \"ix\": 5\n                  },\n                  \"r\": 1,\n                  \"nm\": \"Fill 1\",\n                  \"mn\": \"ADBE Vector Graphic - Fill\",\n                  \"hd\": false\n                },\n                {\n                  \"ty\": \"tr\",\n                  \"p\": {\n                    \"a\": 0,\n                    \"k\": [0, 0],\n                    \"ix\": 2\n                  },\n                  \"a\": {\n                    \"a\": 0,\n                    \"k\": [0, 0],\n                    \"ix\": 1\n                  },\n                  \"s\": {\n                    \"a\": 0,\n                    \"k\": [100, 100],\n                    \"ix\": 3\n                  },\n                  \"r\": {\n                    \"a\": 0,\n                    \"k\": 0,\n                    \"ix\": 6\n                  },\n                  \"o\": {\n                    \"a\": 0,\n                    \"k\": 100,\n                    \"ix\": 7\n                  },\n                  \"sk\": {\n                    \"a\": 0,\n                    \"k\": 0,\n                    \"ix\": 4\n                  },\n                  \"sa\": {\n                    \"a\": 0,\n                    \"k\": 0,\n                    \"ix\": 5\n                  },\n                  \"nm\": \"Transform\"\n                }\n              ],\n              \"nm\": \"Group 1\",\n              \"np\": 3,\n              \"cix\": 2,\n              \"ix\": 1,\n              \"mn\": \"ADBE Vector Group\",\n              \"hd\": false\n            }\n          ],\n          \"ip\": 0,\n          \"op\": 100,\n          \"st\": 0,\n          \"bm\": 0\n        }\n      ]\n    }\n  ],\n  \"layers\": [\n    {\n      \"ddd\": 0,\n      \"ind\": 1,\n      \"ty\": 0,\n      \"nm\": \"f2 pre\",\n      \"parent\": 2,\n      \"td\": 1,\n      \"refId\": \"comp_0\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [500, 500, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"w\": 1000,\n      \"h\": 1000,\n      \"ip\": 0,\n      \"op\": 89,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 2,\n      \"ty\": 4,\n      \"nm\": \"Слой 6\",\n      \"tt\": 2,\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": [0.367],\n                \"y\": [0.987]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p367_0p987_0p333_0\"],\n              \"t\": 0,\n              \"s\": [0],\n              \"e\": [-6]\n            },\n            {\n              \"i\": {\n                \"x\": [0.417],\n                \"y\": [1.009]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p417_1p009_0p333_0\"],\n              \"t\": 15,\n              \"s\": [-6],\n              \"e\": [3]\n            },\n            {\n              \"i\": {\n                \"x\": [0.494],\n                \"y\": [0.974]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p494_0p974_0p333_0\"],\n              \"t\": 33,\n              \"s\": [3],\n              \"e\": [-1]\n            },\n            {\n              \"i\": {\n                \"x\": [0.494],\n                \"y\": [1.104]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p494_1p104_0p333_0\"],\n              \"t\": 59,\n              \"s\": [-1],\n              \"e\": [0]\n            },\n            {\n              \"t\": 85\n            }\n          ],\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [311, 626, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 300, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.292,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p292_1_0p333_0\",\n                    \"t\": 3,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -165.685],\n                          [165.685, 0],\n                          [0, 165.685],\n                          [-165.685, 0]\n                        ],\n                        \"o\": [\n                          [0, 165.685],\n                          [-165.685, 0],\n                          [0, -165.685],\n                          [165.685, 0]\n                        ],\n                        \"v\": [\n                          [300, 0],\n                          [0, 300],\n                          [-300, 0],\n                          [0, -300]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -118.286],\n                          [150.221, 0],\n                          [0, 118.286],\n                          [-150.221, 0]\n                        ],\n                        \"o\": [\n                          [0, 118.286],\n                          [-150.221, 0],\n                          [0, -118.286],\n                          [150.221, 0]\n                        ],\n                        \"v\": [\n                          [272, 57.825],\n                          [0, 272],\n                          [-272, 57.825],\n                          [0, -156.35]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.456,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p456_1_0p333_0\",\n                    \"t\": 18,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -118.286],\n                          [150.221, 0],\n                          [0, 118.286],\n                          [-150.221, 0]\n                        ],\n                        \"o\": [\n                          [0, 118.286],\n                          [-150.221, 0],\n                          [0, -118.286],\n                          [150.221, 0]\n                        ],\n                        \"v\": [\n                          [272, 57.825],\n                          [0, 272],\n                          [-272, 57.825],\n                          [0, -156.35]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -161.78],\n                          [178.308, 0],\n                          [0, 161.78],\n                          [-178.308, 0]\n                        ],\n                        \"o\": [\n                          [0, 161.78],\n                          [-178.308, 0],\n                          [0, -161.78],\n                          [178.308, 0]\n                        ],\n                        \"v\": [\n                          [322.854, -29.926],\n                          [0, 263.003],\n                          [-322.854, -29.926],\n                          [0, -322.854]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.52,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p52_1_0p333_0\",\n                    \"t\": 36,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -161.78],\n                          [178.308, 0],\n                          [0, 161.78],\n                          [-178.308, 0]\n                        ],\n                        \"o\": [\n                          [0, 161.78],\n                          [-178.308, 0],\n                          [0, -161.78],\n                          [178.308, 0]\n                        ],\n                        \"v\": [\n                          [322.854, -29.926],\n                          [0, 263.003],\n                          [-322.854, -29.926],\n                          [0, -322.854]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -155.744],\n                          [155.744, 0],\n                          [0, 155.744],\n                          [-155.744, 0]\n                        ],\n                        \"o\": [\n                          [0, 155.744],\n                          [-155.744, 0],\n                          [0, -155.744],\n                          [155.744, 0]\n                        ],\n                        \"v\": [\n                          [282, 0],\n                          [0, 282],\n                          [-282, 0],\n                          [0, -282]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.498,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p498_1_0p333_0\",\n                    \"t\": 62,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -155.744],\n                          [155.744, 0],\n                          [0, 155.744],\n                          [-155.744, 0]\n                        ],\n                        \"o\": [\n                          [0, 155.744],\n                          [-155.744, 0],\n                          [0, -155.744],\n                          [155.744, 0]\n                        ],\n                        \"v\": [\n                          [282, 0],\n                          [0, 282],\n                          [-282, 0],\n                          [0, -282]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -165.685],\n                          [165.685, 0],\n                          [0, 165.685],\n                          [-165.685, 0]\n                        ],\n                        \"o\": [\n                          [0, 165.685],\n                          [-165.685, 0],\n                          [0, -165.685],\n                          [165.685, 0]\n                        ],\n                        \"v\": [\n                          [300, 0],\n                          [0, 300],\n                          [-300, 0],\n                          [0, -300]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 88\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 89,\n      \"st\": 0,\n      \"bm\": 0\n    }\n  ],\n  \"markers\": []\n}\n"
  },
  {
    "path": "app/assets/lotties/lottieflow-social-networks-15-4-FFFFFF-easey.json",
    "content": "{\n  \"v\": \"5.3.4\",\n  \"fr\": 60,\n  \"ip\": 0,\n  \"op\": 89,\n  \"w\": 654,\n  \"h\": 634,\n  \"nm\": \"F2\",\n  \"ddd\": 0,\n  \"assets\": [\n    {\n      \"id\": \"comp_0\",\n      \"layers\": [\n        {\n          \"ddd\": 0,\n          \"ind\": 1,\n          \"ty\": 4,\n          \"nm\": \"Слой 5\",\n          \"sr\": 1,\n          \"ks\": {\n            \"o\": {\n              \"a\": 0,\n              \"k\": 100,\n              \"ix\": 11\n            },\n            \"r\": {\n              \"a\": 0,\n              \"k\": 0,\n              \"ix\": 10\n            },\n            \"p\": {\n              \"a\": 0,\n              \"k\": [500, 500, 0],\n              \"ix\": 2\n            },\n            \"a\": {\n              \"a\": 0,\n              \"k\": [0, 0, 0],\n              \"ix\": 1\n            },\n            \"s\": {\n              \"a\": 0,\n              \"k\": [100, 100, 100],\n              \"ix\": 6\n            }\n          },\n          \"ao\": 0,\n          \"shapes\": [\n            {\n              \"ty\": \"gr\",\n              \"it\": [\n                {\n                  \"ind\": 0,\n                  \"ty\": \"sh\",\n                  \"ix\": 1,\n                  \"ks\": {\n                    \"a\": 1,\n                    \"k\": [\n                      {\n                        \"i\": {\n                          \"x\": 0.254,\n                          \"y\": 0.998\n                        },\n                        \"o\": {\n                          \"x\": 0.333,\n                          \"y\": 0\n                        },\n                        \"n\": \"0p254_0p998_0p333_0\",\n                        \"t\": 3,\n                        \"s\": [\n                          {\n                            \"i\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-37.267, 0],\n                              [0, 0],\n                              [0, 0],\n                              [32.823, 0],\n                              [0, -75.188],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-15.948, 0],\n                              [-15.275, 2.397],\n                              [0, 0]\n                            ],\n                            \"o\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, -23.725],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-68.527, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [15.275, 2.397],\n                              [15.948, 0],\n                              [0, 0],\n                              [0, 0]\n                            ],\n                            \"v\": [\n                              [116.777, 88.541],\n                              [130.078, 1.822],\n                              [46.875, 1.822],\n                              [46.875, -54.454],\n                              [95.764, -101.303],\n                              [133.594, -101.303],\n                              [133.594, -175.131],\n                              [66.439, -180.99],\n                              [-46.875, -64.271],\n                              [-46.875, 1.822],\n                              [-123.047, 1.822],\n                              [-123.047, 88.541],\n                              [-46.875, 88.541],\n                              [-46.875, 296.346],\n                              [0, 300],\n                              [46.875, 296.346],\n                              [46.875, 88.541]\n                            ],\n                            \"c\": true\n                          }\n                        ],\n                        \"e\": [\n                          {\n                            \"i\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-21.671, 0],\n                              [0, 0],\n                              [0, 0],\n                              [19.087, 0],\n                              [0, -43.723],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-9.274, 0],\n                              [-8.882, 1.394],\n                              [0, 0]\n                            ],\n                            \"o\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, -13.796],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-39.85, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [8.882, 1.394],\n                              [9.274, 0],\n                              [0, 0],\n                              [0, 0]\n                            ],\n                            \"v\": [\n                              [69.173, 147.374],\n                              [76.908, 96.945],\n                              [28.523, 96.945],\n                              [28.523, 64.219],\n                              [56.953, 36.976],\n                              [78.952, 36.976],\n                              [78.952, -5.957],\n                              [39.9, -9.364],\n                              [-25.994, 58.51],\n                              [-25.994, 96.945],\n                              [-70.29, 96.945],\n                              [-70.29, 147.374],\n                              [-25.994, 147.374],\n                              [-25.994, 268.216],\n                              [1.265, 270.341],\n                              [28.523, 268.216],\n                              [28.523, 147.374]\n                            ],\n                            \"c\": true\n                          }\n                        ]\n                      },\n                      {\n                        \"i\": {\n                          \"x\": 0.323,\n                          \"y\": 0.998\n                        },\n                        \"o\": {\n                          \"x\": 0.333,\n                          \"y\": 0\n                        },\n                        \"n\": \"0p323_0p998_0p333_0\",\n                        \"t\": 18,\n                        \"s\": [\n                          {\n                            \"i\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-21.671, 0],\n                              [0, 0],\n                              [0, 0],\n                              [19.087, 0],\n                              [0, -43.723],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-9.274, 0],\n                              [-8.882, 1.394],\n                              [0, 0]\n                            ],\n                            \"o\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, -13.796],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-39.85, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [8.882, 1.394],\n                              [9.274, 0],\n                              [0, 0],\n                              [0, 0]\n                            ],\n                            \"v\": [\n                              [69.173, 147.374],\n                              [76.908, 96.945],\n                              [28.523, 96.945],\n                              [28.523, 64.219],\n                              [56.953, 36.976],\n                              [78.952, 36.976],\n                              [78.952, -5.957],\n                              [39.9, -9.364],\n                              [-25.994, 58.51],\n                              [-25.994, 96.945],\n                              [-70.29, 96.945],\n                              [-70.29, 147.374],\n                              [-25.994, 147.374],\n                              [-25.994, 268.216],\n                              [1.265, 270.341],\n                              [28.523, 268.216],\n                              [28.523, 147.374]\n                            ],\n                            \"c\": true\n                          }\n                        ],\n                        \"e\": [\n                          {\n                            \"i\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-33.166, 0],\n                              [0, 0],\n                              [0, 0],\n                              [29.211, 0],\n                              [0, -66.914],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-14.193, 0],\n                              [-13.594, 2.133],\n                              [0, 0]\n                            ],\n                            \"o\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, -21.114],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-60.986, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [13.594, 2.133],\n                              [14.193, 0],\n                              [0, 0],\n                              [0, 0]\n                            ],\n                            \"v\": [\n                              [103.566, 73.814],\n                              [115.403, -3.363],\n                              [41.355, -3.363],\n                              [41.355, -53.447],\n                              [84.865, -95.141],\n                              [118.532, -95.141],\n                              [118.532, -160.845],\n                              [58.767, -166.06],\n                              [-42.079, -62.184],\n                              [-42.079, -3.363],\n                              [-109.87, -3.363],\n                              [-109.87, 73.814],\n                              [-42.079, 73.814],\n                              [-42.079, 258.754],\n                              [-0.362, 262.006],\n                              [41.355, 258.754],\n                              [41.355, 73.814]\n                            ],\n                            \"c\": true\n                          }\n                        ]\n                      },\n                      {\n                        \"i\": {\n                          \"x\": 0.398,\n                          \"y\": 0.998\n                        },\n                        \"o\": {\n                          \"x\": 0.333,\n                          \"y\": 0\n                        },\n                        \"n\": \"0p398_0p998_0p333_0\",\n                        \"t\": 36,\n                        \"s\": [\n                          {\n                            \"i\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-33.166, 0],\n                              [0, 0],\n                              [0, 0],\n                              [29.211, 0],\n                              [0, -66.914],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-14.193, 0],\n                              [-13.594, 2.133],\n                              [0, 0]\n                            ],\n                            \"o\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, -21.114],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-60.986, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [13.594, 2.133],\n                              [14.193, 0],\n                              [0, 0],\n                              [0, 0]\n                            ],\n                            \"v\": [\n                              [103.566, 73.814],\n                              [115.403, -3.363],\n                              [41.355, -3.363],\n                              [41.355, -53.447],\n                              [84.865, -95.141],\n                              [118.532, -95.141],\n                              [118.532, -160.845],\n                              [58.767, -166.06],\n                              [-42.079, -62.184],\n                              [-42.079, -3.363],\n                              [-109.87, -3.363],\n                              [-109.87, 73.814],\n                              [-42.079, 73.814],\n                              [-42.079, 258.754],\n                              [-0.362, 262.006],\n                              [41.355, 258.754],\n                              [41.355, 73.814]\n                            ],\n                            \"c\": true\n                          }\n                        ],\n                        \"e\": [\n                          {\n                            \"i\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-30.303, 0],\n                              [0, 0],\n                              [0, 0],\n                              [26.689, 0],\n                              [0, -61.137],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-12.967, 0],\n                              [-12.42, 1.949],\n                              [0, 0]\n                            ],\n                            \"o\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, -19.291],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-55.721, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [12.42, 1.949],\n                              [12.967, 0],\n                              [0, 0],\n                              [0, 0]\n                            ],\n                            \"v\": [\n                              [95.94, 109.041],\n                              [106.756, 38.528],\n                              [39.101, 38.528],\n                              [39.101, -7.232],\n                              [78.854, -45.326],\n                              [109.614, -45.326],\n                              [109.614, -105.358],\n                              [55.009, -110.122],\n                              [-37.13, -15.215],\n                              [-37.13, 38.528],\n                              [-99.067, 38.528],\n                              [-99.067, 109.041],\n                              [-37.13, 109.041],\n                              [-37.13, 278.014],\n                              [0.985, 280.984],\n                              [39.101, 278.014],\n                              [39.101, 109.041]\n                            ],\n                            \"c\": true\n                          }\n                        ]\n                      },\n                      {\n                        \"i\": {\n                          \"x\": 0.357,\n                          \"y\": 0.998\n                        },\n                        \"o\": {\n                          \"x\": 0.333,\n                          \"y\": 0\n                        },\n                        \"n\": \"0p357_0p998_0p333_0\",\n                        \"t\": 62,\n                        \"s\": [\n                          {\n                            \"i\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-30.303, 0],\n                              [0, 0],\n                              [0, 0],\n                              [26.689, 0],\n                              [0, -61.137],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-12.967, 0],\n                              [-12.42, 1.949],\n                              [0, 0]\n                            ],\n                            \"o\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, -19.291],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-55.721, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [12.42, 1.949],\n                              [12.967, 0],\n                              [0, 0],\n                              [0, 0]\n                            ],\n                            \"v\": [\n                              [95.94, 109.041],\n                              [106.756, 38.528],\n                              [39.101, 38.528],\n                              [39.101, -7.232],\n                              [78.854, -45.326],\n                              [109.614, -45.326],\n                              [109.614, -105.358],\n                              [55.009, -110.122],\n                              [-37.13, -15.215],\n                              [-37.13, 38.528],\n                              [-99.067, 38.528],\n                              [-99.067, 109.041],\n                              [-37.13, 109.041],\n                              [-37.13, 278.014],\n                              [0.985, 280.984],\n                              [39.101, 278.014],\n                              [39.101, 109.041]\n                            ],\n                            \"c\": true\n                          }\n                        ],\n                        \"e\": [\n                          {\n                            \"i\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-37.267, 0],\n                              [0, 0],\n                              [0, 0],\n                              [32.823, 0],\n                              [0, -75.188],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-15.948, 0],\n                              [-15.275, 2.397],\n                              [0, 0]\n                            ],\n                            \"o\": [\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, -23.725],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [-68.527, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [0, 0],\n                              [15.275, 2.397],\n                              [15.948, 0],\n                              [0, 0],\n                              [0, 0]\n                            ],\n                            \"v\": [\n                              [116.777, 88.541],\n                              [130.078, 1.822],\n                              [46.875, 1.822],\n                              [46.875, -54.454],\n                              [95.764, -101.303],\n                              [133.594, -101.303],\n                              [133.594, -175.131],\n                              [66.439, -180.99],\n                              [-46.875, -64.271],\n                              [-46.875, 1.822],\n                              [-123.047, 1.822],\n                              [-123.047, 88.541],\n                              [-46.875, 88.541],\n                              [-46.875, 296.346],\n                              [0, 300],\n                              [46.875, 296.346],\n                              [46.875, 88.541]\n                            ],\n                            \"c\": true\n                          }\n                        ]\n                      },\n                      {\n                        \"t\": 88\n                      }\n                    ],\n                    \"ix\": 2\n                  },\n                  \"nm\": \"Path 1\",\n                  \"mn\": \"ADBE Vector Shape - Group\",\n                  \"hd\": false\n                },\n                {\n                  \"ty\": \"st\",\n                  \"c\": {\n                    \"a\": 0,\n                    \"k\": [1, 1, 1, 1],\n                    \"ix\": 3\n                  },\n                  \"o\": {\n                    \"a\": 0,\n                    \"k\": 100,\n                    \"ix\": 4\n                  },\n                  \"w\": {\n                    \"a\": 0,\n                    \"k\": 1,\n                    \"ix\": 5\n                  },\n                  \"lc\": 1,\n                  \"lj\": 1,\n                  \"ml\": 10,\n                  \"ml2\": {\n                    \"a\": 0,\n                    \"k\": 10,\n                    \"ix\": 8\n                  },\n                  \"nm\": \"Stroke 1\",\n                  \"mn\": \"ADBE Vector Graphic - Stroke\",\n                  \"hd\": false\n                },\n                {\n                  \"ty\": \"fl\",\n                  \"c\": {\n                    \"a\": 0,\n                    \"k\": [1, 1, 1, 1],\n                    \"ix\": 4\n                  },\n                  \"o\": {\n                    \"a\": 0,\n                    \"k\": 100,\n                    \"ix\": 5\n                  },\n                  \"r\": 1,\n                  \"nm\": \"Fill 1\",\n                  \"mn\": \"ADBE Vector Graphic - Fill\",\n                  \"hd\": false\n                },\n                {\n                  \"ty\": \"tr\",\n                  \"p\": {\n                    \"a\": 0,\n                    \"k\": [0, 0],\n                    \"ix\": 2\n                  },\n                  \"a\": {\n                    \"a\": 0,\n                    \"k\": [0, 0],\n                    \"ix\": 1\n                  },\n                  \"s\": {\n                    \"a\": 0,\n                    \"k\": [100, 100],\n                    \"ix\": 3\n                  },\n                  \"r\": {\n                    \"a\": 0,\n                    \"k\": 0,\n                    \"ix\": 6\n                  },\n                  \"o\": {\n                    \"a\": 0,\n                    \"k\": 100,\n                    \"ix\": 7\n                  },\n                  \"sk\": {\n                    \"a\": 0,\n                    \"k\": 0,\n                    \"ix\": 4\n                  },\n                  \"sa\": {\n                    \"a\": 0,\n                    \"k\": 0,\n                    \"ix\": 5\n                  },\n                  \"nm\": \"Transform\"\n                }\n              ],\n              \"nm\": \"Group 1\",\n              \"np\": 3,\n              \"cix\": 2,\n              \"ix\": 1,\n              \"mn\": \"ADBE Vector Group\",\n              \"hd\": false\n            }\n          ],\n          \"ip\": 0,\n          \"op\": 100,\n          \"st\": 0,\n          \"bm\": 0\n        }\n      ]\n    }\n  ],\n  \"layers\": [\n    {\n      \"ddd\": 0,\n      \"ind\": 1,\n      \"ty\": 0,\n      \"nm\": \"f2 pre\",\n      \"parent\": 2,\n      \"td\": 1,\n      \"refId\": \"comp_0\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [500, 500, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"w\": 1000,\n      \"h\": 1000,\n      \"ip\": 0,\n      \"op\": 89,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 2,\n      \"ty\": 4,\n      \"nm\": \"Слой 6\",\n      \"tt\": 2,\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": [0.367],\n                \"y\": [0.987]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p367_0p987_0p333_0\"],\n              \"t\": 0,\n              \"s\": [0],\n              \"e\": [-6]\n            },\n            {\n              \"i\": {\n                \"x\": [0.417],\n                \"y\": [1.009]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p417_1p009_0p333_0\"],\n              \"t\": 15,\n              \"s\": [-6],\n              \"e\": [3]\n            },\n            {\n              \"i\": {\n                \"x\": [0.494],\n                \"y\": [0.974]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p494_0p974_0p333_0\"],\n              \"t\": 33,\n              \"s\": [3],\n              \"e\": [-1]\n            },\n            {\n              \"i\": {\n                \"x\": [0.494],\n                \"y\": [1.104]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p494_1p104_0p333_0\"],\n              \"t\": 59,\n              \"s\": [-1],\n              \"e\": [0]\n            },\n            {\n              \"t\": 85\n            }\n          ],\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [311, 626, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 300, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.292,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p292_1_0p333_0\",\n                    \"t\": 3,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -165.685],\n                          [165.685, 0],\n                          [0, 165.685],\n                          [-165.685, 0]\n                        ],\n                        \"o\": [\n                          [0, 165.685],\n                          [-165.685, 0],\n                          [0, -165.685],\n                          [165.685, 0]\n                        ],\n                        \"v\": [\n                          [300, 0],\n                          [0, 300],\n                          [-300, 0],\n                          [0, -300]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -118.286],\n                          [150.221, 0],\n                          [0, 118.286],\n                          [-150.221, 0]\n                        ],\n                        \"o\": [\n                          [0, 118.286],\n                          [-150.221, 0],\n                          [0, -118.286],\n                          [150.221, 0]\n                        ],\n                        \"v\": [\n                          [272, 57.825],\n                          [0, 272],\n                          [-272, 57.825],\n                          [0, -156.35]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.456,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p456_1_0p333_0\",\n                    \"t\": 18,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -118.286],\n                          [150.221, 0],\n                          [0, 118.286],\n                          [-150.221, 0]\n                        ],\n                        \"o\": [\n                          [0, 118.286],\n                          [-150.221, 0],\n                          [0, -118.286],\n                          [150.221, 0]\n                        ],\n                        \"v\": [\n                          [272, 57.825],\n                          [0, 272],\n                          [-272, 57.825],\n                          [0, -156.35]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -161.78],\n                          [178.308, 0],\n                          [0, 161.78],\n                          [-178.308, 0]\n                        ],\n                        \"o\": [\n                          [0, 161.78],\n                          [-178.308, 0],\n                          [0, -161.78],\n                          [178.308, 0]\n                        ],\n                        \"v\": [\n                          [322.854, -29.926],\n                          [0, 263.003],\n                          [-322.854, -29.926],\n                          [0, -322.854]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.52,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p52_1_0p333_0\",\n                    \"t\": 36,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -161.78],\n                          [178.308, 0],\n                          [0, 161.78],\n                          [-178.308, 0]\n                        ],\n                        \"o\": [\n                          [0, 161.78],\n                          [-178.308, 0],\n                          [0, -161.78],\n                          [178.308, 0]\n                        ],\n                        \"v\": [\n                          [322.854, -29.926],\n                          [0, 263.003],\n                          [-322.854, -29.926],\n                          [0, -322.854]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -155.744],\n                          [155.744, 0],\n                          [0, 155.744],\n                          [-155.744, 0]\n                        ],\n                        \"o\": [\n                          [0, 155.744],\n                          [-155.744, 0],\n                          [0, -155.744],\n                          [155.744, 0]\n                        ],\n                        \"v\": [\n                          [282, 0],\n                          [0, 282],\n                          [-282, 0],\n                          [0, -282]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.498,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p498_1_0p333_0\",\n                    \"t\": 62,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -155.744],\n                          [155.744, 0],\n                          [0, 155.744],\n                          [-155.744, 0]\n                        ],\n                        \"o\": [\n                          [0, 155.744],\n                          [-155.744, 0],\n                          [0, -155.744],\n                          [155.744, 0]\n                        ],\n                        \"v\": [\n                          [282, 0],\n                          [0, 282],\n                          [-282, 0],\n                          [0, -282]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -165.685],\n                          [165.685, 0],\n                          [0, 165.685],\n                          [-165.685, 0]\n                        ],\n                        \"o\": [\n                          [0, 165.685],\n                          [-165.685, 0],\n                          [0, -165.685],\n                          [165.685, 0]\n                        ],\n                        \"v\": [\n                          [300, 0],\n                          [0, 300],\n                          [-300, 0],\n                          [0, -300]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 88\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 89,\n      \"st\": 0,\n      \"bm\": 0\n    }\n  ],\n  \"markers\": []\n}\n"
  },
  {
    "path": "app/assets/lotties/lottieflow-social-networks-15-5-000000-easey.json",
    "content": "{\n  \"v\": \"5.3.4\",\n  \"fr\": 60,\n  \"ip\": 0,\n  \"op\": 71,\n  \"w\": 584,\n  \"h\": 598,\n  \"nm\": \"inst ver 2\",\n  \"ddd\": 0,\n  \"assets\": [],\n  \"layers\": [\n    {\n      \"ddd\": 0,\n      \"ind\": 1,\n      \"ty\": 4,\n      \"nm\": \"Слой 13\",\n      \"parent\": 3,\n      \"td\": 1,\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [65.63, 0],\n                    [0, 65.63],\n                    [-65.631, 0],\n                    [0, -65.631]\n                  ],\n                  \"o\": [\n                    [-65.631, 0],\n                    [0, -65.631],\n                    [65.63, 0],\n                    [0, 65.63]\n                  ],\n                  \"v\": [\n                    [0.001, 118.834],\n                    [-118.834, 0.001],\n                    [0.001, -118.834],\n                    [118.834, 0.001]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ind\": 1,\n              \"ty\": \"sh\",\n              \"ix\": 2,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [1.217, 26.665],\n                    [6.18, 15.902],\n                    [13.898, 13.898],\n                    [16.44, 6.389],\n                    [26.61, 1.215],\n                    [67.895, 0],\n                    [26.665, -1.217],\n                    [15.903, -6.181],\n                    [13.898, -13.898],\n                    [6.389, -16.44],\n                    [1.215, -26.61],\n                    [0, -67.896],\n                    [-1.217, -26.665],\n                    [-6.181, -15.902],\n                    [-13.898, -13.899],\n                    [-16.44, -6.389],\n                    [-26.61, -1.214],\n                    [-67.896, 0],\n                    [-26.665, 1.217],\n                    [-15.902, 6.18],\n                    [-13.899, 13.898],\n                    [-6.389, 16.44],\n                    [-1.214, 26.61],\n                    [0, 67.895]\n                  ],\n                  \"o\": [\n                    [-1.214, -26.61],\n                    [-6.389, -16.44],\n                    [-13.899, -13.898],\n                    [-15.902, -6.181],\n                    [-26.665, -1.217],\n                    [-67.896, 0],\n                    [-26.61, 1.215],\n                    [-16.44, 6.389],\n                    [-13.898, 13.898],\n                    [-6.181, 15.902],\n                    [-1.217, 26.665],\n                    [0, 67.895],\n                    [1.215, 26.61],\n                    [6.389, 16.44],\n                    [13.898, 13.898],\n                    [15.903, 6.18],\n                    [26.665, 1.217],\n                    [67.895, 0],\n                    [26.61, -1.214],\n                    [16.44, -6.389],\n                    [13.898, -13.899],\n                    [6.18, -15.902],\n                    [1.217, -26.665],\n                    [0, -67.896]\n                  ],\n                  \"v\": [\n                    [248.496, -103.075],\n                    [236.876, -163.76],\n                    [208.04, -208.039],\n                    [163.759, -236.875],\n                    [103.075, -248.496],\n                    [0, -250],\n                    [-103.075, -248.496],\n                    [-163.76, -236.875],\n                    [-208.04, -208.039],\n                    [-236.876, -163.76],\n                    [-248.497, -103.075],\n                    [-250, 0],\n                    [-248.497, 103.075],\n                    [-236.876, 163.759],\n                    [-208.04, 208.04],\n                    [-163.76, 236.876],\n                    [-103.075, 248.496],\n                    [0, 250],\n                    [103.075, 248.496],\n                    [163.759, 236.876],\n                    [208.04, 208.04],\n                    [236.876, 163.759],\n                    [248.496, 103.075],\n                    [250, 0]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 2\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 3,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 71,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 2,\n      \"ty\": 4,\n      \"nm\": \"Слой 17\",\n      \"parent\": 3,\n      \"tt\": 2,\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": [0.542],\n                \"y\": [0.988]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p542_0p988_0p333_0\"],\n              \"t\": 15,\n              \"s\": [182],\n              \"e\": [-9]\n            },\n            {\n              \"i\": {\n                \"x\": [0.564],\n                \"y\": [1.014]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p564_1p014_0p333_0\"],\n              \"t\": 31,\n              \"s\": [-9],\n              \"e\": [182]\n            },\n            {\n              \"t\": 52\n            }\n          ],\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [0, -97.667, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, -83.667, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.542,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p542_1_0p333_0\",\n                    \"t\": 18,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -46.208],\n                          [46.208, 0],\n                          [0, 46.208],\n                          [-46.208, 0]\n                        ],\n                        \"o\": [\n                          [0, 46.208],\n                          [-46.208, 0],\n                          [0, -46.208],\n                          [46.208, 0]\n                        ],\n                        \"v\": [\n                          [83.667, 0],\n                          [0, 83.667],\n                          [-83.667, 0],\n                          [0, -83.667]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -59.67],\n                          [58.836, 0],\n                          [0, 59.67],\n                          [-58.836, 0]\n                        ],\n                        \"o\": [\n                          [0, 59.67],\n                          [-58.836, 0],\n                          [0, -59.67],\n                          [58.836, 0]\n                        ],\n                        \"v\": [\n                          [83.667, 24.376],\n                          [-22.865, 132.418],\n                          [-129.396, 24.376],\n                          [-22.865, -83.667]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_1_0p333_0\",\n                    \"t\": 34,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -59.67],\n                          [58.836, 0],\n                          [0, 59.67],\n                          [-58.836, 0]\n                        ],\n                        \"o\": [\n                          [0, 59.67],\n                          [-58.836, 0],\n                          [0, -59.67],\n                          [58.836, 0]\n                        ],\n                        \"v\": [\n                          [83.667, 24.376],\n                          [-22.865, 132.418],\n                          [-129.396, 24.376],\n                          [-22.865, -83.667]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -46.208],\n                          [46.208, 0],\n                          [0, 46.208],\n                          [-46.208, 0]\n                        ],\n                        \"o\": [\n                          [0, 46.208],\n                          [-46.208, 0],\n                          [0, -46.208],\n                          [46.208, 0]\n                        ],\n                        \"v\": [\n                          [83.667, 0],\n                          [0, 83.667],\n                          [-83.667, 0],\n                          [0, -83.667]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 55\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 1,\n                \"ix\": 5\n              },\n              \"lc\": 1,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 3,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 21,\n      \"op\": 42,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 3,\n      \"ty\": 4,\n      \"nm\": \"Слой 16\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": [0.404],\n                \"y\": [1]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p404_1_0p333_0\"],\n              \"t\": 5,\n              \"s\": [0],\n              \"e\": [-9]\n            },\n            {\n              \"i\": {\n                \"x\": [0.421],\n                \"y\": [1]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p421_1_0p333_0\"],\n              \"t\": 20,\n              \"s\": [-9],\n              \"e\": [8]\n            },\n            {\n              \"i\": {\n                \"x\": [0.479],\n                \"y\": [1]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p479_1_0p333_0\"],\n              \"t\": 36,\n              \"s\": [8],\n              \"e\": [-3]\n            },\n            {\n              \"i\": {\n                \"x\": [0.421],\n                \"y\": [1]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p421_1_0p333_0\"],\n              \"t\": 57,\n              \"s\": [-3],\n              \"e\": [0]\n            },\n            {\n              \"t\": 73\n            }\n          ],\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.404,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p404_1_0p333_0\",\n              \"t\": 6,\n              \"s\": [290, 280, 0],\n              \"e\": [290, 272, 0],\n              \"to\": [0, -1.33333337306976, 0],\n              \"ti\": [0, -5.66666650772095, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.421,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p421_1_0p333_0\",\n              \"t\": 21,\n              \"s\": [290, 272, 0],\n              \"e\": [290, 314, 0],\n              \"to\": [0, 5.66666650772095, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.479,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p479_1_0p333_0\",\n              \"t\": 37,\n              \"s\": [290, 314, 0],\n              \"e\": [290, 272, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 5.66666650772095, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.421,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p421_1_0p333_0\",\n              \"t\": 58,\n              \"s\": [290, 272, 0],\n              \"e\": [290, 280, 0],\n              \"to\": [0, -5.66666650772095, 0],\n              \"ti\": [0, -1.33333337306976, 0]\n            },\n            {\n              \"t\": 74\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.479,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p479_1_0p333_0\",\n                    \"t\": 1,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -16.569],\n                          [16.569, 0],\n                          [0, 16.569],\n                          [-16.568, 0]\n                        ],\n                        \"o\": [\n                          [0, 16.569],\n                          [-16.568, 0],\n                          [0, -16.569],\n                          [16.569, 0]\n                        ],\n                        \"v\": [\n                          [163.451, -133.45],\n                          [133.45, -103.451],\n                          [103.451, -133.45],\n                          [133.45, -163.45]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -15.291],\n                          [15.291, 0],\n                          [0, 15.291],\n                          [-15.29, 0]\n                        ],\n                        \"o\": [\n                          [0, 15.291],\n                          [-15.29, 0],\n                          [0, -15.291],\n                          [15.291, 0]\n                        ],\n                        \"v\": [\n                          [150.842, -123.156],\n                          [123.156, -95.47],\n                          [95.47, -123.156],\n                          [123.156, -150.841]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 16,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -15.291],\n                          [15.291, 0],\n                          [0, 15.291],\n                          [-15.29, 0]\n                        ],\n                        \"o\": [\n                          [0, 15.291],\n                          [-15.29, 0],\n                          [0, -15.291],\n                          [15.291, 0]\n                        ],\n                        \"v\": [\n                          [150.842, -123.156],\n                          [123.156, -95.47],\n                          [95.47, -123.156],\n                          [123.156, -150.841]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -18.097],\n                          [18.097, 0],\n                          [0, 18.097],\n                          [-18.096, 0]\n                        ],\n                        \"o\": [\n                          [0, 18.097],\n                          [-18.096, 0],\n                          [0, -18.097],\n                          [18.097, 0]\n                        ],\n                        \"v\": [\n                          [178.526, -145.758],\n                          [145.758, -112.992],\n                          [112.992, -145.758],\n                          [145.758, -178.525]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.533,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p533_1_0p333_0\",\n                    \"t\": 32,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -18.097],\n                          [18.097, 0],\n                          [0, 18.097],\n                          [-18.096, 0]\n                        ],\n                        \"o\": [\n                          [0, 18.097],\n                          [-18.096, 0],\n                          [0, -18.097],\n                          [18.097, 0]\n                        ],\n                        \"v\": [\n                          [178.526, -145.758],\n                          [145.758, -112.992],\n                          [112.992, -145.758],\n                          [145.758, -178.525]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -15.049],\n                          [15.049, 0],\n                          [0, 15.049],\n                          [-15.049, 0]\n                        ],\n                        \"o\": [\n                          [0, 15.049],\n                          [-15.049, 0],\n                          [0, -15.049],\n                          [15.049, 0]\n                        ],\n                        \"v\": [\n                          [148.463, -121.214],\n                          [121.214, -93.965],\n                          [93.965, -121.214],\n                          [121.214, -148.462]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 53,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -15.049],\n                          [15.049, 0],\n                          [0, 15.049],\n                          [-15.049, 0]\n                        ],\n                        \"o\": [\n                          [0, 15.049],\n                          [-15.049, 0],\n                          [0, -15.049],\n                          [15.049, 0]\n                        ],\n                        \"v\": [\n                          [148.463, -121.214],\n                          [121.214, -93.965],\n                          [93.965, -121.214],\n                          [121.214, -148.462]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -16.569],\n                          [16.569, 0],\n                          [0, 16.569],\n                          [-16.568, 0]\n                        ],\n                        \"o\": [\n                          [0, 16.569],\n                          [-16.568, 0],\n                          [0, -16.569],\n                          [16.569, 0]\n                        ],\n                        \"v\": [\n                          [163.451, -133.45],\n                          [133.45, -103.451],\n                          [103.451, -133.45],\n                          [133.45, -163.45]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 69\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0.011764706112, 0.015686275437, 0.015686275437, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        },\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.479,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p479_1_0p333_0\",\n                    \"t\": 1,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [70.901, 0],\n                          [0, -70.902],\n                          [-70.902, 0],\n                          [0, 70.901]\n                        ],\n                        \"o\": [\n                          [-70.902, 0],\n                          [0, 70.901],\n                          [70.901, 0],\n                          [0, -70.902]\n                        ],\n                        \"v\": [\n                          [0.001, -128.378],\n                          [-128.378, 0.001],\n                          [0.001, 128.378],\n                          [128.378, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [65.432, 0],\n                          [0, -65.433],\n                          [-65.433, 0],\n                          [0, 65.432]\n                        ],\n                        \"o\": [\n                          [-65.433, 0],\n                          [0, 65.432],\n                          [65.432, 0],\n                          [0, -65.433]\n                        ],\n                        \"v\": [\n                          [0, -118.475],\n                          [-118.475, 0],\n                          [0, 118.475],\n                          [118.475, 0]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 16,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [65.432, 0],\n                          [0, -65.433],\n                          [-65.433, 0],\n                          [0, 65.432]\n                        ],\n                        \"o\": [\n                          [-65.433, 0],\n                          [0, 65.432],\n                          [65.432, 0],\n                          [0, -65.433]\n                        ],\n                        \"v\": [\n                          [0, -118.475],\n                          [-118.475, 0],\n                          [0, 118.475],\n                          [118.475, 0]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [77.44, 0],\n                          [0, -77.441],\n                          [-77.442, 0],\n                          [0, 77.44]\n                        ],\n                        \"o\": [\n                          [-77.442, 0],\n                          [0, 77.44],\n                          [77.44, 0],\n                          [0, -77.441]\n                        ],\n                        \"v\": [\n                          [0.001, -140.218],\n                          [-140.218, 0.001],\n                          [0.001, 140.218],\n                          [140.218, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.265,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p265_1_0p333_0\",\n                    \"t\": 32,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [77.44, 0],\n                          [0, -77.441],\n                          [-77.442, 0],\n                          [0, 77.44]\n                        ],\n                        \"o\": [\n                          [-77.442, 0],\n                          [0, 77.44],\n                          [77.44, 0],\n                          [0, -77.441]\n                        ],\n                        \"v\": [\n                          [0.001, -140.218],\n                          [-140.218, 0.001],\n                          [0.001, 140.218],\n                          [140.218, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [70.205, 0],\n                          [0, -70.206],\n                          [-70.206, 0],\n                          [0, 70.205]\n                        ],\n                        \"o\": [\n                          [-70.206, 0],\n                          [0, 70.205],\n                          [70.205, 0],\n                          [0, -70.206]\n                        ],\n                        \"v\": [\n                          [0.001, -127.117],\n                          [-127.117, 0.001],\n                          [0.001, 127.117],\n                          [127.117, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 53,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [70.205, 0],\n                          [0, -70.206],\n                          [-70.206, 0],\n                          [0, 70.205]\n                        ],\n                        \"o\": [\n                          [-70.206, 0],\n                          [0, 70.205],\n                          [70.205, 0],\n                          [0, -70.206]\n                        ],\n                        \"v\": [\n                          [0.001, -127.117],\n                          [-127.117, 0.001],\n                          [0.001, 127.117],\n                          [127.117, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [70.901, 0],\n                          [0, -70.902],\n                          [-70.902, 0],\n                          [0, 70.901]\n                        ],\n                        \"o\": [\n                          [-70.902, 0],\n                          [0, 70.901],\n                          [70.901, 0],\n                          [0, -70.902]\n                        ],\n                        \"v\": [\n                          [0.001, -128.378],\n                          [-128.378, 0.001],\n                          [0.001, 128.378],\n                          [128.378, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 69\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ind\": 1,\n              \"ty\": \"sh\",\n              \"ix\": 2,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.479,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p479_1_0p333_0\",\n                    \"t\": 1,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [46.023, 0],\n                          [0, 46.023],\n                          [-46.024, 0],\n                          [0, -46.024]\n                        ],\n                        \"o\": [\n                          [-46.024, 0],\n                          [0, -46.024],\n                          [46.023, 0],\n                          [0, 46.023]\n                        ],\n                        \"v\": [\n                          [0.001, 83.333],\n                          [-83.333, 0.001],\n                          [0.001, -83.333],\n                          [83.333, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [42.473, 0],\n                          [0, 42.473],\n                          [-42.474, 0],\n                          [0, -42.474]\n                        ],\n                        \"o\": [\n                          [-42.474, 0],\n                          [0, -42.474],\n                          [42.473, 0],\n                          [0, 42.473]\n                        ],\n                        \"v\": [\n                          [0, 76.905],\n                          [-76.905, 0],\n                          [0, -76.905],\n                          [76.905, 0]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 16,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [42.473, 0],\n                          [0, 42.473],\n                          [-42.474, 0],\n                          [0, -42.474]\n                        ],\n                        \"o\": [\n                          [-42.474, 0],\n                          [0, -42.474],\n                          [42.473, 0],\n                          [0, 42.473]\n                        ],\n                        \"v\": [\n                          [0, 76.905],\n                          [-76.905, 0],\n                          [0, -76.905],\n                          [76.905, 0]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [50.268, 0],\n                          [0, 50.268],\n                          [-50.269, 0],\n                          [0, -50.269]\n                        ],\n                        \"o\": [\n                          [-50.269, 0],\n                          [0, -50.269],\n                          [50.268, 0],\n                          [0, 50.268]\n                        ],\n                        \"v\": [\n                          [0.001, 91.019],\n                          [-91.019, 0.001],\n                          [0.001, -91.019],\n                          [91.019, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.265,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p265_1_0p333_0\",\n                    \"t\": 32,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [50.268, 0],\n                          [0, 50.268],\n                          [-50.269, 0],\n                          [0, -50.269]\n                        ],\n                        \"o\": [\n                          [-50.269, 0],\n                          [0, -50.269],\n                          [50.268, 0],\n                          [0, 50.268]\n                        ],\n                        \"v\": [\n                          [0.001, 91.019],\n                          [-91.019, 0.001],\n                          [0.001, -91.019],\n                          [91.019, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [45.571, 0],\n                          [0, 45.571],\n                          [-45.572, 0],\n                          [0, -45.572]\n                        ],\n                        \"o\": [\n                          [-45.572, 0],\n                          [0, -45.572],\n                          [45.571, 0],\n                          [0, 45.571]\n                        ],\n                        \"v\": [\n                          [0, 82.515],\n                          [-82.515, 0],\n                          [0, -82.515],\n                          [82.515, 0]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 53,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [45.571, 0],\n                          [0, 45.571],\n                          [-45.572, 0],\n                          [0, -45.572]\n                        ],\n                        \"o\": [\n                          [-45.572, 0],\n                          [0, -45.572],\n                          [45.571, 0],\n                          [0, 45.571]\n                        ],\n                        \"v\": [\n                          [0, 82.515],\n                          [-82.515, 0],\n                          [0, -82.515],\n                          [82.515, 0]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [46.023, 0],\n                          [0, 46.023],\n                          [-46.024, 0],\n                          [0, -46.024]\n                        ],\n                        \"o\": [\n                          [-46.024, 0],\n                          [0, -46.024],\n                          [46.023, 0],\n                          [0, 46.023]\n                        ],\n                        \"v\": [\n                          [0.001, 83.333],\n                          [-83.333, 0.001],\n                          [0.001, -83.333],\n                          [83.333, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 69\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 2\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0.011764706112, 0.015686275437, 0.015686275437, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 2\",\n          \"np\": 3,\n          \"cix\": 2,\n          \"ix\": 2,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        },\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.479,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p479_1_0p333_0\",\n                    \"t\": 0,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [-66.753, 0],\n                          [-26.361, -1.203],\n                          [-8.809, -3.423],\n                          [-8.748, -8.748],\n                          [-4.535, -11.668],\n                          [-1.111, -24.375],\n                          [0, -66.753],\n                          [1.203, -26.362],\n                          [3.424, -8.809],\n                          [8.749, -8.748],\n                          [11.669, -4.535],\n                          [24.375, -1.111],\n                          [66.757, 0],\n                          [26.359, 1.203],\n                          [8.809, 3.424],\n                          [8.749, 8.749],\n                          [4.534, 11.669],\n                          [1.112, 24.375],\n                          [0, 66.752],\n                          [-1.203, 26.361],\n                          [-3.424, 8.809],\n                          [-8.748, 8.748],\n                          [-11.669, 4.535],\n                          [-24.375, 1.112]\n                        ],\n                        \"o\": [\n                          [66.752, 0],\n                          [24.375, 1.112],\n                          [11.669, 4.535],\n                          [8.749, 8.748],\n                          [3.424, 8.809],\n                          [1.203, 26.361],\n                          [0, 66.752],\n                          [-1.111, 24.375],\n                          [-4.535, 11.669],\n                          [-8.748, 8.749],\n                          [-8.809, 3.424],\n                          [-26.358, 1.203],\n                          [-66.758, 0],\n                          [-24.375, -1.111],\n                          [-11.669, -4.535],\n                          [-8.748, -8.748],\n                          [-3.424, -8.809],\n                          [-1.203, -26.362],\n                          [0, -66.753],\n                          [1.112, -24.375],\n                          [4.534, -11.668],\n                          [8.749, -8.748],\n                          [8.809, -3.423],\n                          [26.361, -1.203]\n                        ],\n                        \"v\": [\n                          [0.001, -204.954],\n                          [101.021, -203.497],\n                          [147.443, -194.889],\n                          [176.188, -176.188],\n                          [194.889, -147.443],\n                          [203.497, -101.021],\n                          [204.955, 0.001],\n                          [203.497, 101.021],\n                          [194.889, 147.443],\n                          [176.188, 176.188],\n                          [147.443, 194.889],\n                          [101.021, 203.497],\n                          [0.001, 204.955],\n                          [-101.021, 203.497],\n                          [-147.443, 194.889],\n                          [-176.189, 176.188],\n                          [-194.889, 147.443],\n                          [-203.498, 101.021],\n                          [-204.955, 0.001],\n                          [-203.498, -101.021],\n                          [-194.889, -147.443],\n                          [-176.189, -176.188],\n                          [-147.443, -194.889],\n                          [-101.021, -203.497]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [-61.604, 0],\n                          [-24.328, -1.11],\n                          [-8.13, -3.159],\n                          [-8.073, -8.072],\n                          [-4.185, -10.768],\n                          [-1.026, -22.494],\n                          [0, -61.604],\n                          [1.109, -24.328],\n                          [3.16, -8.13],\n                          [8.073, -8.074],\n                          [10.769, -4.185],\n                          [22.494, -1.026],\n                          [61.607, 0],\n                          [24.325, 1.109],\n                          [8.13, 3.159],\n                          [8.074, 8.074],\n                          [4.185, 10.769],\n                          [1.026, 22.494],\n                          [0, 61.603],\n                          [-1.11, 24.328],\n                          [-3.159, 8.13],\n                          [-8.073, 8.072],\n                          [-10.77, 4.184],\n                          [-22.494, 1.026]\n                        ],\n                        \"o\": [\n                          [61.603, 0],\n                          [22.494, 1.027],\n                          [10.769, 4.185],\n                          [8.074, 8.073],\n                          [3.16, 8.13],\n                          [1.11, 24.328],\n                          [0, 61.603],\n                          [-1.026, 22.494],\n                          [-4.185, 10.769],\n                          [-8.073, 8.074],\n                          [-8.13, 3.16],\n                          [-24.324, 1.11],\n                          [-61.608, 0],\n                          [-22.494, -1.026],\n                          [-10.769, -4.185],\n                          [-8.073, -8.073],\n                          [-3.16, -8.13],\n                          [-1.11, -24.328],\n                          [0, -61.604],\n                          [1.027, -22.494],\n                          [4.184, -10.768],\n                          [8.074, -8.073],\n                          [8.13, -3.159],\n                          [24.328, -1.11]\n                        ],\n                        \"v\": [\n                          [0, -189.143],\n                          [93.228, -187.799],\n                          [136.069, -179.855],\n                          [162.596, -162.596],\n                          [179.855, -136.069],\n                          [187.799, -93.228],\n                          [189.144, 0],\n                          [187.799, 93.228],\n                          [179.855, 136.069],\n                          [162.596, 162.596],\n                          [136.069, 179.855],\n                          [93.228, 187.799],\n                          [0, 189.144],\n                          [-93.228, 187.799],\n                          [-136.069, 179.855],\n                          [-162.597, 162.596],\n                          [-179.855, 136.069],\n                          [-187.8, 93.228],\n                          [-189.144, 0],\n                          [-187.8, -93.228],\n                          [-179.855, -136.069],\n                          [-162.597, -162.596],\n                          [-136.069, -179.855],\n                          [-93.228, -187.799]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 15,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [-61.604, 0],\n                          [-24.328, -1.11],\n                          [-8.13, -3.159],\n                          [-8.073, -8.072],\n                          [-4.185, -10.768],\n                          [-1.026, -22.494],\n                          [0, -61.604],\n                          [1.109, -24.328],\n                          [3.16, -8.13],\n                          [8.073, -8.074],\n                          [10.769, -4.185],\n                          [22.494, -1.026],\n                          [61.607, 0],\n                          [24.325, 1.109],\n                          [8.13, 3.159],\n                          [8.074, 8.074],\n                          [4.185, 10.769],\n                          [1.026, 22.494],\n                          [0, 61.603],\n                          [-1.11, 24.328],\n                          [-3.159, 8.13],\n                          [-8.073, 8.072],\n                          [-10.77, 4.184],\n                          [-22.494, 1.026]\n                        ],\n                        \"o\": [\n                          [61.603, 0],\n                          [22.494, 1.027],\n                          [10.769, 4.185],\n                          [8.074, 8.073],\n                          [3.16, 8.13],\n                          [1.11, 24.328],\n                          [0, 61.603],\n                          [-1.026, 22.494],\n                          [-4.185, 10.769],\n                          [-8.073, 8.074],\n                          [-8.13, 3.16],\n                          [-24.324, 1.11],\n                          [-61.608, 0],\n                          [-22.494, -1.026],\n                          [-10.769, -4.185],\n                          [-8.073, -8.073],\n                          [-3.16, -8.13],\n                          [-1.11, -24.328],\n                          [0, -61.604],\n                          [1.027, -22.494],\n                          [4.184, -10.768],\n                          [8.074, -8.073],\n                          [8.13, -3.159],\n                          [24.328, -1.11]\n                        ],\n                        \"v\": [\n                          [0, -189.143],\n                          [93.228, -187.799],\n                          [136.069, -179.855],\n                          [162.596, -162.596],\n                          [179.855, -136.069],\n                          [187.799, -93.228],\n                          [189.144, 0],\n                          [187.799, 93.228],\n                          [179.855, 136.069],\n                          [162.596, 162.596],\n                          [136.069, 179.855],\n                          [93.228, 187.799],\n                          [0, 189.144],\n                          [-93.228, 187.799],\n                          [-136.069, 179.855],\n                          [-162.597, 162.596],\n                          [-179.855, 136.069],\n                          [-187.8, 93.228],\n                          [-189.144, 0],\n                          [-187.8, -93.228],\n                          [-179.855, -136.069],\n                          [-162.597, -162.596],\n                          [-136.069, -179.855],\n                          [-93.228, -187.799]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [-72.91, 0],\n                          [-28.793, -1.314],\n                          [-9.622, -3.739],\n                          [-9.555, -9.554],\n                          [-4.953, -12.745],\n                          [-1.215, -26.623],\n                          [0, -72.91],\n                          [1.313, -28.793],\n                          [3.739, -9.622],\n                          [9.555, -9.556],\n                          [12.746, -4.953],\n                          [26.623, -1.215],\n                          [72.914, 0],\n                          [28.79, 1.313],\n                          [9.622, 3.739],\n                          [9.556, 9.556],\n                          [4.953, 12.745],\n                          [1.215, 26.623],\n                          [0, 72.909],\n                          [-1.314, 28.793],\n                          [-3.739, 9.622],\n                          [-9.555, 9.554],\n                          [-12.746, 4.952],\n                          [-26.623, 1.215]\n                        ],\n                        \"o\": [\n                          [72.909, 0],\n                          [26.623, 1.215],\n                          [12.746, 4.953],\n                          [9.556, 9.555],\n                          [3.739, 9.622],\n                          [1.314, 28.793],\n                          [0, 72.909],\n                          [-1.214, 26.623],\n                          [-4.953, 12.746],\n                          [-9.555, 9.556],\n                          [-9.622, 3.74],\n                          [-28.788, 1.314],\n                          [-72.915, 0],\n                          [-26.623, -1.214],\n                          [-12.746, -4.953],\n                          [-9.555, -9.555],\n                          [-3.739, -9.622],\n                          [-1.314, -28.793],\n                          [0, -72.91],\n                          [1.215, -26.623],\n                          [4.952, -12.745],\n                          [9.556, -9.555],\n                          [9.622, -3.738],\n                          [28.793, -1.314]\n                        ],\n                        \"v\": [\n                          [0.001, -223.857],\n                          [110.338, -222.265],\n                          [161.041, -212.864],\n                          [192.438, -192.437],\n                          [212.864, -161.041],\n                          [222.265, -110.338],\n                          [223.858, 0.001],\n                          [222.265, 110.338],\n                          [212.864, 161.041],\n                          [192.438, 192.437],\n                          [161.041, 212.864],\n                          [110.338, 222.265],\n                          [0.001, 223.858],\n                          [-110.338, 222.265],\n                          [-161.041, 212.864],\n                          [-192.439, 192.437],\n                          [-212.864, 161.041],\n                          [-222.266, 110.338],\n                          [-223.858, 0.001],\n                          [-222.266, -110.338],\n                          [-212.864, -161.041],\n                          [-192.439, -192.437],\n                          [-161.041, -212.864],\n                          [-110.338, -222.265]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.533,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p533_1_0p333_0\",\n                    \"t\": 31,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [-72.91, 0],\n                          [-28.793, -1.314],\n                          [-9.622, -3.739],\n                          [-9.555, -9.554],\n                          [-4.953, -12.745],\n                          [-1.215, -26.623],\n                          [0, -72.91],\n                          [1.313, -28.793],\n                          [3.739, -9.622],\n                          [9.555, -9.556],\n                          [12.746, -4.953],\n                          [26.623, -1.215],\n                          [72.914, 0],\n                          [28.79, 1.313],\n                          [9.622, 3.739],\n                          [9.556, 9.556],\n                          [4.953, 12.745],\n                          [1.215, 26.623],\n                          [0, 72.909],\n                          [-1.314, 28.793],\n                          [-3.739, 9.622],\n                          [-9.555, 9.554],\n                          [-12.746, 4.952],\n                          [-26.623, 1.215]\n                        ],\n                        \"o\": [\n                          [72.909, 0],\n                          [26.623, 1.215],\n                          [12.746, 4.953],\n                          [9.556, 9.555],\n                          [3.739, 9.622],\n                          [1.314, 28.793],\n                          [0, 72.909],\n                          [-1.214, 26.623],\n                          [-4.953, 12.746],\n                          [-9.555, 9.556],\n                          [-9.622, 3.74],\n                          [-28.788, 1.314],\n                          [-72.915, 0],\n                          [-26.623, -1.214],\n                          [-12.746, -4.953],\n                          [-9.555, -9.555],\n                          [-3.739, -9.622],\n                          [-1.314, -28.793],\n                          [0, -72.91],\n                          [1.215, -26.623],\n                          [4.952, -12.745],\n                          [9.556, -9.555],\n                          [9.622, -3.738],\n                          [28.793, -1.314]\n                        ],\n                        \"v\": [\n                          [0.001, -223.857],\n                          [110.338, -222.265],\n                          [161.041, -212.864],\n                          [192.438, -192.437],\n                          [212.864, -161.041],\n                          [222.265, -110.338],\n                          [223.858, 0.001],\n                          [222.265, 110.338],\n                          [212.864, 161.041],\n                          [192.438, 192.437],\n                          [161.041, 212.864],\n                          [110.338, 222.265],\n                          [0.001, 223.858],\n                          [-110.338, 222.265],\n                          [-161.041, 212.864],\n                          [-192.439, 192.437],\n                          [-212.864, 161.041],\n                          [-222.266, 110.338],\n                          [-223.858, 0.001],\n                          [-222.266, -110.338],\n                          [-212.864, -161.041],\n                          [-192.439, -192.437],\n                          [-161.041, -212.864],\n                          [-110.338, -222.265]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [-60.632, 0],\n                          [-23.944, -1.093],\n                          [-8.001, -3.11],\n                          [-7.946, -7.945],\n                          [-4.119, -10.599],\n                          [-1.01, -22.14],\n                          [0, -60.632],\n                          [1.092, -23.944],\n                          [3.11, -8.002],\n                          [7.946, -7.947],\n                          [10.599, -4.119],\n                          [22.14, -1.01],\n                          [60.636, 0],\n                          [23.942, 1.092],\n                          [8.002, 3.11],\n                          [7.947, 7.947],\n                          [4.119, 10.599],\n                          [1.01, 22.14],\n                          [0, 60.631],\n                          [-1.093, 23.944],\n                          [-3.109, 8.002],\n                          [-7.946, 7.945],\n                          [-10.6, 4.118],\n                          [-22.14, 1.01]\n                        ],\n                        \"o\": [\n                          [60.631, 0],\n                          [22.14, 1.01],\n                          [10.599, 4.119],\n                          [7.947, 7.946],\n                          [3.11, 8.002],\n                          [1.092, 23.944],\n                          [0, 60.631],\n                          [-1.01, 22.14],\n                          [-4.119, 10.599],\n                          [-7.946, 7.947],\n                          [-8.002, 3.11],\n                          [-23.941, 1.092],\n                          [-60.637, 0],\n                          [-22.14, -1.01],\n                          [-10.599, -4.119],\n                          [-7.946, -7.946],\n                          [-3.11, -8.002],\n                          [-1.092, -23.944],\n                          [0, -60.632],\n                          [1.01, -22.14],\n                          [4.118, -10.598],\n                          [7.947, -7.946],\n                          [8.002, -3.109],\n                          [23.944, -1.092]\n                        ],\n                        \"v\": [\n                          [0, -186.161],\n                          [91.758, -184.838],\n                          [133.923, -177.019],\n                          [160.033, -160.033],\n                          [177.019, -133.923],\n                          [184.838, -91.758],\n                          [186.162, 0],\n                          [184.838, 91.758],\n                          [177.019, 133.923],\n                          [160.033, 160.033],\n                          [133.923, 177.019],\n                          [91.758, 184.838],\n                          [0, 186.162],\n                          [-91.758, 184.838],\n                          [-133.923, 177.019],\n                          [-160.033, 160.033],\n                          [-177.019, 133.923],\n                          [-184.839, 91.758],\n                          [-186.162, 0],\n                          [-184.839, -91.758],\n                          [-177.019, -133.923],\n                          [-160.033, -160.033],\n                          [-133.923, -177.019],\n                          [-91.758, -184.838]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 52,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [-60.632, 0],\n                          [-23.944, -1.093],\n                          [-8.001, -3.11],\n                          [-7.946, -7.945],\n                          [-4.119, -10.599],\n                          [-1.01, -22.14],\n                          [0, -60.632],\n                          [1.092, -23.944],\n                          [3.11, -8.002],\n                          [7.946, -7.947],\n                          [10.599, -4.119],\n                          [22.14, -1.01],\n                          [60.636, 0],\n                          [23.942, 1.092],\n                          [8.002, 3.11],\n                          [7.947, 7.947],\n                          [4.119, 10.599],\n                          [1.01, 22.14],\n                          [0, 60.631],\n                          [-1.093, 23.944],\n                          [-3.109, 8.002],\n                          [-7.946, 7.945],\n                          [-10.6, 4.118],\n                          [-22.14, 1.01]\n                        ],\n                        \"o\": [\n                          [60.631, 0],\n                          [22.14, 1.01],\n                          [10.599, 4.119],\n                          [7.947, 7.946],\n                          [3.11, 8.002],\n                          [1.092, 23.944],\n                          [0, 60.631],\n                          [-1.01, 22.14],\n                          [-4.119, 10.599],\n                          [-7.946, 7.947],\n                          [-8.002, 3.11],\n                          [-23.941, 1.092],\n                          [-60.637, 0],\n                          [-22.14, -1.01],\n                          [-10.599, -4.119],\n                          [-7.946, -7.946],\n                          [-3.11, -8.002],\n                          [-1.092, -23.944],\n                          [0, -60.632],\n                          [1.01, -22.14],\n                          [4.118, -10.598],\n                          [7.947, -7.946],\n                          [8.002, -3.109],\n                          [23.944, -1.092]\n                        ],\n                        \"v\": [\n                          [0, -186.161],\n                          [91.758, -184.838],\n                          [133.923, -177.019],\n                          [160.033, -160.033],\n                          [177.019, -133.923],\n                          [184.838, -91.758],\n                          [186.162, 0],\n                          [184.838, 91.758],\n                          [177.019, 133.923],\n                          [160.033, 160.033],\n                          [133.923, 177.019],\n                          [91.758, 184.838],\n                          [0, 186.162],\n                          [-91.758, 184.838],\n                          [-133.923, 177.019],\n                          [-160.033, 160.033],\n                          [-177.019, 133.923],\n                          [-184.839, 91.758],\n                          [-186.162, 0],\n                          [-184.839, -91.758],\n                          [-177.019, -133.923],\n                          [-160.033, -160.033],\n                          [-133.923, -177.019],\n                          [-91.758, -184.838]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [-66.753, 0],\n                          [-26.361, -1.203],\n                          [-8.809, -3.423],\n                          [-8.748, -8.748],\n                          [-4.535, -11.668],\n                          [-1.111, -24.375],\n                          [0, -66.753],\n                          [1.203, -26.362],\n                          [3.424, -8.809],\n                          [8.749, -8.748],\n                          [11.669, -4.535],\n                          [24.375, -1.111],\n                          [66.757, 0],\n                          [26.359, 1.203],\n                          [8.809, 3.424],\n                          [8.749, 8.749],\n                          [4.534, 11.669],\n                          [1.112, 24.375],\n                          [0, 66.752],\n                          [-1.203, 26.361],\n                          [-3.424, 8.809],\n                          [-8.748, 8.748],\n                          [-11.669, 4.535],\n                          [-24.375, 1.112]\n                        ],\n                        \"o\": [\n                          [66.752, 0],\n                          [24.375, 1.112],\n                          [11.669, 4.535],\n                          [8.749, 8.748],\n                          [3.424, 8.809],\n                          [1.203, 26.361],\n                          [0, 66.752],\n                          [-1.111, 24.375],\n                          [-4.535, 11.669],\n                          [-8.748, 8.749],\n                          [-8.809, 3.424],\n                          [-26.358, 1.203],\n                          [-66.758, 0],\n                          [-24.375, -1.111],\n                          [-11.669, -4.535],\n                          [-8.748, -8.748],\n                          [-3.424, -8.809],\n                          [-1.203, -26.362],\n                          [0, -66.753],\n                          [1.112, -24.375],\n                          [4.534, -11.668],\n                          [8.749, -8.748],\n                          [8.809, -3.423],\n                          [26.361, -1.203]\n                        ],\n                        \"v\": [\n                          [0.001, -204.954],\n                          [101.021, -203.497],\n                          [147.443, -194.889],\n                          [176.188, -176.188],\n                          [194.889, -147.443],\n                          [203.497, -101.021],\n                          [204.955, 0.001],\n                          [203.497, 101.021],\n                          [194.889, 147.443],\n                          [176.188, 176.188],\n                          [147.443, 194.889],\n                          [101.021, 203.497],\n                          [0.001, 204.955],\n                          [-101.021, 203.497],\n                          [-147.443, 194.889],\n                          [-176.189, 176.188],\n                          [-194.889, 147.443],\n                          [-203.498, 101.021],\n                          [-204.955, 0.001],\n                          [-203.498, -101.021],\n                          [-194.889, -147.443],\n                          [-176.189, -176.188],\n                          [-147.443, -194.889],\n                          [-101.021, -203.497]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 68\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ind\": 1,\n              \"ty\": \"sh\",\n              \"ix\": 2,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.479,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p479_1_0p333_0\",\n                    \"t\": 0,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [67.895, 0],\n                          [26.665, -1.217],\n                          [15.903, -6.181],\n                          [13.898, -13.898],\n                          [6.389, -16.44],\n                          [1.215, -26.61],\n                          [0, -67.896],\n                          [-1.217, -26.665],\n                          [-6.181, -15.902],\n                          [-13.898, -13.899],\n                          [-16.44, -6.389],\n                          [-26.61, -1.214],\n                          [-67.896, 0],\n                          [-26.665, 1.217],\n                          [-15.902, 6.18],\n                          [-13.899, 13.898],\n                          [-6.389, 16.44],\n                          [-1.214, 26.61],\n                          [0, 67.895],\n                          [1.217, 26.665],\n                          [6.18, 15.902],\n                          [13.898, 13.898],\n                          [16.44, 6.389],\n                          [26.61, 1.215]\n                        ],\n                        \"o\": [\n                          [-67.896, 0],\n                          [-26.61, 1.215],\n                          [-16.44, 6.389],\n                          [-13.898, 13.898],\n                          [-6.181, 15.902],\n                          [-1.217, 26.665],\n                          [0, 67.895],\n                          [1.215, 26.61],\n                          [6.389, 16.44],\n                          [13.898, 13.898],\n                          [15.903, 6.18],\n                          [26.665, 1.217],\n                          [67.895, 0],\n                          [26.61, -1.214],\n                          [16.44, -6.389],\n                          [13.898, -13.899],\n                          [6.18, -15.902],\n                          [1.217, -26.665],\n                          [0, -67.896],\n                          [-1.214, -26.61],\n                          [-6.389, -16.44],\n                          [-13.899, -13.898],\n                          [-15.902, -6.181],\n                          [-26.665, -1.217]\n                        ],\n                        \"v\": [\n                          [0.001, -250],\n                          [-103.075, -248.496],\n                          [-163.76, -236.875],\n                          [-208.04, -208.039],\n                          [-236.876, -163.76],\n                          [-248.497, -103.075],\n                          [-250, 0.001],\n                          [-248.497, 103.075],\n                          [-236.876, 163.759],\n                          [-208.04, 208.04],\n                          [-163.76, 236.876],\n                          [-103.075, 248.496],\n                          [0.001, 250],\n                          [103.075, 248.496],\n                          [163.759, 236.876],\n                          [208.04, 208.04],\n                          [236.876, 163.759],\n                          [248.496, 103.075],\n                          [250, 0.001],\n                          [248.496, -103.075],\n                          [236.876, -163.76],\n                          [208.04, -208.039],\n                          [163.759, -236.875],\n                          [103.075, -248.496]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [62.658, 0],\n                          [24.608, -1.123],\n                          [14.676, -5.704],\n                          [12.826, -12.826],\n                          [5.897, -15.171],\n                          [1.12, -24.557],\n                          [0, -62.659],\n                          [-1.123, -24.608],\n                          [-5.703, -14.675],\n                          [-12.827, -12.827],\n                          [-15.172, -5.895],\n                          [-24.557, -1.12],\n                          [-62.659, 0],\n                          [-24.608, 1.122],\n                          [-14.675, 5.703],\n                          [-12.826, 12.827],\n                          [-5.896, 15.172],\n                          [-1.12, 24.557],\n                          [0, 62.658],\n                          [1.122, 24.608],\n                          [5.703, 14.675],\n                          [12.827, 12.826],\n                          [15.171, 5.897],\n                          [24.557, 1.12]\n                        ],\n                        \"o\": [\n                          [-62.659, 0],\n                          [-24.557, 1.121],\n                          [-15.172, 5.896],\n                          [-12.826, 12.826],\n                          [-5.704, 14.675],\n                          [-1.123, 24.608],\n                          [0, 62.658],\n                          [1.121, 24.557],\n                          [5.896, 15.172],\n                          [12.826, 12.826],\n                          [14.676, 5.703],\n                          [24.608, 1.123],\n                          [62.658, 0],\n                          [24.557, -1.12],\n                          [15.172, -5.896],\n                          [12.826, -12.827],\n                          [5.703, -14.675],\n                          [1.123, -24.608],\n                          [0, -62.659],\n                          [-1.12, -24.557],\n                          [-5.896, -15.171],\n                          [-12.827, -12.826],\n                          [-14.675, -5.704],\n                          [-24.608, -1.123]\n                        ],\n                        \"v\": [\n                          [0, -230.714],\n                          [-95.123, -229.326],\n                          [-151.128, -218.602],\n                          [-191.992, -191.991],\n                          [-218.603, -151.127],\n                          [-229.327, -95.123],\n                          [-230.714, 0],\n                          [-229.327, 95.123],\n                          [-218.603, 151.127],\n                          [-191.992, 191.992],\n                          [-151.128, 218.603],\n                          [-95.123, 229.326],\n                          [0, 230.714],\n                          [95.123, 229.326],\n                          [151.127, 218.603],\n                          [191.992, 191.992],\n                          [218.603, 151.127],\n                          [229.326, 95.123],\n                          [230.714, 0],\n                          [229.326, -95.123],\n                          [218.603, -151.127],\n                          [191.992, -191.991],\n                          [151.127, -218.602],\n                          [95.123, -229.326]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 15,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [62.658, 0],\n                          [24.608, -1.123],\n                          [14.676, -5.704],\n                          [12.826, -12.826],\n                          [5.897, -15.171],\n                          [1.12, -24.557],\n                          [0, -62.659],\n                          [-1.123, -24.608],\n                          [-5.703, -14.675],\n                          [-12.827, -12.827],\n                          [-15.172, -5.895],\n                          [-24.557, -1.12],\n                          [-62.659, 0],\n                          [-24.608, 1.122],\n                          [-14.675, 5.703],\n                          [-12.826, 12.827],\n                          [-5.896, 15.172],\n                          [-1.12, 24.557],\n                          [0, 62.658],\n                          [1.122, 24.608],\n                          [5.703, 14.675],\n                          [12.827, 12.826],\n                          [15.171, 5.897],\n                          [24.557, 1.12]\n                        ],\n                        \"o\": [\n                          [-62.659, 0],\n                          [-24.557, 1.121],\n                          [-15.172, 5.896],\n                          [-12.826, 12.826],\n                          [-5.704, 14.675],\n                          [-1.123, 24.608],\n                          [0, 62.658],\n                          [1.121, 24.557],\n                          [5.896, 15.172],\n                          [12.826, 12.826],\n                          [14.676, 5.703],\n                          [24.608, 1.123],\n                          [62.658, 0],\n                          [24.557, -1.12],\n                          [15.172, -5.896],\n                          [12.826, -12.827],\n                          [5.703, -14.675],\n                          [1.123, -24.608],\n                          [0, -62.659],\n                          [-1.12, -24.557],\n                          [-5.896, -15.171],\n                          [-12.827, -12.826],\n                          [-14.675, -5.704],\n                          [-24.608, -1.123]\n                        ],\n                        \"v\": [\n                          [0, -230.714],\n                          [-95.123, -229.326],\n                          [-151.128, -218.602],\n                          [-191.992, -191.991],\n                          [-218.603, -151.127],\n                          [-229.327, -95.123],\n                          [-230.714, 0],\n                          [-229.327, 95.123],\n                          [-218.603, 151.127],\n                          [-191.992, 191.992],\n                          [-151.128, 218.603],\n                          [-95.123, 229.326],\n                          [0, 230.714],\n                          [95.123, 229.326],\n                          [151.127, 218.603],\n                          [191.992, 191.992],\n                          [218.603, 151.127],\n                          [229.326, 95.123],\n                          [230.714, 0],\n                          [229.326, -95.123],\n                          [218.603, -151.127],\n                          [191.992, -191.991],\n                          [151.127, -218.602],\n                          [95.123, -229.326]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [74.157, 0],\n                          [29.124, -1.329],\n                          [17.37, -6.75],\n                          [15.18, -15.18],\n                          [6.979, -17.956],\n                          [1.326, -29.064],\n                          [0, -74.158],\n                          [-1.329, -29.124],\n                          [-6.75, -17.369],\n                          [-15.181, -15.181],\n                          [-17.956, -6.977],\n                          [-29.064, -1.326],\n                          [-74.158, 0],\n                          [-29.124, 1.328],\n                          [-17.368, 6.75],\n                          [-15.18, 15.181],\n                          [-6.978, 17.956],\n                          [-1.326, 29.064],\n                          [0, 74.157],\n                          [1.328, 29.124],\n                          [6.75, 17.368],\n                          [15.181, 15.179],\n                          [17.956, 6.979],\n                          [29.064, 1.326]\n                        ],\n                        \"o\": [\n                          [-74.158, 0],\n                          [-29.064, 1.327],\n                          [-17.956, 6.978],\n                          [-15.18, 15.18],\n                          [-6.751, 17.368],\n                          [-1.329, 29.124],\n                          [0, 74.157],\n                          [1.327, 29.064],\n                          [6.978, 17.956],\n                          [15.18, 15.18],\n                          [17.37, 6.749],\n                          [29.124, 1.329],\n                          [74.157, 0],\n                          [29.064, -1.326],\n                          [17.956, -6.978],\n                          [15.18, -15.181],\n                          [6.75, -17.368],\n                          [1.329, -29.124],\n                          [0, -74.158],\n                          [-1.326, -29.064],\n                          [-6.978, -17.956],\n                          [-15.181, -15.18],\n                          [-17.368, -6.751],\n                          [-29.124, -1.329]\n                        ],\n                        \"v\": [\n                          [0.001, -273.057],\n                          [-112.581, -271.414],\n                          [-178.864, -258.721],\n                          [-227.227, -227.226],\n                          [-258.722, -178.863],\n                          [-271.415, -112.581],\n                          [-273.057, 0.001],\n                          [-271.415, 112.581],\n                          [-258.722, 178.863],\n                          [-227.227, 227.227],\n                          [-178.864, 258.722],\n                          [-112.581, 271.414],\n                          [0.001, 273.057],\n                          [112.581, 271.414],\n                          [178.863, 258.722],\n                          [227.228, 227.227],\n                          [258.722, 178.863],\n                          [271.414, 112.581],\n                          [273.057, 0.001],\n                          [271.414, -112.581],\n                          [258.722, -178.863],\n                          [227.228, -227.226],\n                          [178.863, -258.721],\n                          [112.581, -271.414]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.533,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p533_1_0p333_0\",\n                    \"t\": 31,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [74.157, 0],\n                          [29.124, -1.329],\n                          [17.37, -6.75],\n                          [15.18, -15.18],\n                          [6.979, -17.956],\n                          [1.326, -29.064],\n                          [0, -74.158],\n                          [-1.329, -29.124],\n                          [-6.75, -17.369],\n                          [-15.181, -15.181],\n                          [-17.956, -6.977],\n                          [-29.064, -1.326],\n                          [-74.158, 0],\n                          [-29.124, 1.328],\n                          [-17.368, 6.75],\n                          [-15.18, 15.181],\n                          [-6.978, 17.956],\n                          [-1.326, 29.064],\n                          [0, 74.157],\n                          [1.328, 29.124],\n                          [6.75, 17.368],\n                          [15.181, 15.179],\n                          [17.956, 6.979],\n                          [29.064, 1.326]\n                        ],\n                        \"o\": [\n                          [-74.158, 0],\n                          [-29.064, 1.327],\n                          [-17.956, 6.978],\n                          [-15.18, 15.18],\n                          [-6.751, 17.368],\n                          [-1.329, 29.124],\n                          [0, 74.157],\n                          [1.327, 29.064],\n                          [6.978, 17.956],\n                          [15.18, 15.18],\n                          [17.37, 6.749],\n                          [29.124, 1.329],\n                          [74.157, 0],\n                          [29.064, -1.326],\n                          [17.956, -6.978],\n                          [15.18, -15.181],\n                          [6.75, -17.368],\n                          [1.329, -29.124],\n                          [0, -74.158],\n                          [-1.326, -29.064],\n                          [-6.978, -17.956],\n                          [-15.181, -15.18],\n                          [-17.368, -6.751],\n                          [-29.124, -1.329]\n                        ],\n                        \"v\": [\n                          [0.001, -273.057],\n                          [-112.581, -271.414],\n                          [-178.864, -258.721],\n                          [-227.227, -227.226],\n                          [-258.722, -178.863],\n                          [-271.415, -112.581],\n                          [-273.057, 0.001],\n                          [-271.415, 112.581],\n                          [-258.722, 178.863],\n                          [-227.227, 227.227],\n                          [-178.864, 258.722],\n                          [-112.581, 271.414],\n                          [0.001, 273.057],\n                          [112.581, 271.414],\n                          [178.863, 258.722],\n                          [227.228, 227.227],\n                          [258.722, 178.863],\n                          [271.414, 112.581],\n                          [273.057, 0.001],\n                          [271.414, -112.581],\n                          [258.722, -178.863],\n                          [227.228, -227.226],\n                          [178.863, -258.721],\n                          [112.581, -271.414]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [61.67, 0],\n                          [24.22, -1.106],\n                          [14.445, -5.614],\n                          [12.624, -12.624],\n                          [5.804, -14.932],\n                          [1.103, -24.17],\n                          [0, -61.671],\n                          [-1.106, -24.22],\n                          [-5.613, -14.444],\n                          [-12.624, -12.624],\n                          [-14.933, -5.802],\n                          [-24.17, -1.103],\n                          [-61.671, 0],\n                          [-24.22, 1.105],\n                          [-14.444, 5.613],\n                          [-12.624, 12.625],\n                          [-5.803, 14.932],\n                          [-1.103, 24.17],\n                          [0, 61.67],\n                          [1.105, 24.22],\n                          [5.613, 14.444],\n                          [12.624, 12.623],\n                          [14.932, 5.804],\n                          [24.17, 1.103]\n                        ],\n                        \"o\": [\n                          [-61.671, 0],\n                          [-24.17, 1.103],\n                          [-14.932, 5.803],\n                          [-12.624, 12.624],\n                          [-5.614, 14.444],\n                          [-1.105, 24.22],\n                          [0, 61.67],\n                          [1.103, 24.17],\n                          [5.803, 14.932],\n                          [12.624, 12.624],\n                          [14.445, 5.613],\n                          [24.22, 1.105],\n                          [61.67, 0],\n                          [24.17, -1.102],\n                          [14.932, -5.803],\n                          [12.624, -12.625],\n                          [5.613, -14.444],\n                          [1.105, -24.22],\n                          [0, -61.671],\n                          [-1.102, -24.17],\n                          [-5.803, -14.932],\n                          [-12.625, -12.624],\n                          [-14.444, -5.614],\n                          [-24.22, -1.105]\n                        ],\n                        \"v\": [\n                          [0, -227.076],\n                          [-93.623, -225.71],\n                          [-148.745, -215.155],\n                          [-188.964, -188.963],\n                          [-215.156, -148.744],\n                          [-225.711, -93.623],\n                          [-227.076, 0],\n                          [-225.711, 93.623],\n                          [-215.156, 148.744],\n                          [-188.964, 188.964],\n                          [-148.745, 215.156],\n                          [-93.623, 225.71],\n                          [0, 227.076],\n                          [93.623, 225.71],\n                          [148.744, 215.156],\n                          [188.964, 188.964],\n                          [215.156, 148.744],\n                          [225.71, 93.623],\n                          [227.076, 0],\n                          [225.71, -93.623],\n                          [215.156, -148.744],\n                          [188.964, -188.963],\n                          [148.744, -215.155],\n                          [93.623, -225.71]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 52,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [61.67, 0],\n                          [24.22, -1.106],\n                          [14.445, -5.614],\n                          [12.624, -12.624],\n                          [5.804, -14.932],\n                          [1.103, -24.17],\n                          [0, -61.671],\n                          [-1.106, -24.22],\n                          [-5.613, -14.444],\n                          [-12.624, -12.624],\n                          [-14.933, -5.802],\n                          [-24.17, -1.103],\n                          [-61.671, 0],\n                          [-24.22, 1.105],\n                          [-14.444, 5.613],\n                          [-12.624, 12.625],\n                          [-5.803, 14.932],\n                          [-1.103, 24.17],\n                          [0, 61.67],\n                          [1.105, 24.22],\n                          [5.613, 14.444],\n                          [12.624, 12.623],\n                          [14.932, 5.804],\n                          [24.17, 1.103]\n                        ],\n                        \"o\": [\n                          [-61.671, 0],\n                          [-24.17, 1.103],\n                          [-14.932, 5.803],\n                          [-12.624, 12.624],\n                          [-5.614, 14.444],\n                          [-1.105, 24.22],\n                          [0, 61.67],\n                          [1.103, 24.17],\n                          [5.803, 14.932],\n                          [12.624, 12.624],\n                          [14.445, 5.613],\n                          [24.22, 1.105],\n                          [61.67, 0],\n                          [24.17, -1.102],\n                          [14.932, -5.803],\n                          [12.624, -12.625],\n                          [5.613, -14.444],\n                          [1.105, -24.22],\n                          [0, -61.671],\n                          [-1.102, -24.17],\n                          [-5.803, -14.932],\n                          [-12.625, -12.624],\n                          [-14.444, -5.614],\n                          [-24.22, -1.105]\n                        ],\n                        \"v\": [\n                          [0, -227.076],\n                          [-93.623, -225.71],\n                          [-148.745, -215.155],\n                          [-188.964, -188.963],\n                          [-215.156, -148.744],\n                          [-225.711, -93.623],\n                          [-227.076, 0],\n                          [-225.711, 93.623],\n                          [-215.156, 148.744],\n                          [-188.964, 188.964],\n                          [-148.745, 215.156],\n                          [-93.623, 225.71],\n                          [0, 227.076],\n                          [93.623, 225.71],\n                          [148.744, 215.156],\n                          [188.964, 188.964],\n                          [215.156, 148.744],\n                          [225.71, 93.623],\n                          [227.076, 0],\n                          [225.71, -93.623],\n                          [215.156, -148.744],\n                          [188.964, -188.963],\n                          [148.744, -215.155],\n                          [93.623, -225.71]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [67.895, 0],\n                          [26.665, -1.217],\n                          [15.903, -6.181],\n                          [13.898, -13.898],\n                          [6.389, -16.44],\n                          [1.215, -26.61],\n                          [0, -67.896],\n                          [-1.217, -26.665],\n                          [-6.181, -15.902],\n                          [-13.898, -13.899],\n                          [-16.44, -6.389],\n                          [-26.61, -1.214],\n                          [-67.896, 0],\n                          [-26.665, 1.217],\n                          [-15.902, 6.18],\n                          [-13.899, 13.898],\n                          [-6.389, 16.44],\n                          [-1.214, 26.61],\n                          [0, 67.895],\n                          [1.217, 26.665],\n                          [6.18, 15.902],\n                          [13.898, 13.898],\n                          [16.44, 6.389],\n                          [26.61, 1.215]\n                        ],\n                        \"o\": [\n                          [-67.896, 0],\n                          [-26.61, 1.215],\n                          [-16.44, 6.389],\n                          [-13.898, 13.898],\n                          [-6.181, 15.902],\n                          [-1.217, 26.665],\n                          [0, 67.895],\n                          [1.215, 26.61],\n                          [6.389, 16.44],\n                          [13.898, 13.898],\n                          [15.903, 6.18],\n                          [26.665, 1.217],\n                          [67.895, 0],\n                          [26.61, -1.214],\n                          [16.44, -6.389],\n                          [13.898, -13.899],\n                          [6.18, -15.902],\n                          [1.217, -26.665],\n                          [0, -67.896],\n                          [-1.214, -26.61],\n                          [-6.389, -16.44],\n                          [-13.899, -13.898],\n                          [-15.902, -6.181],\n                          [-26.665, -1.217]\n                        ],\n                        \"v\": [\n                          [0.001, -250],\n                          [-103.075, -248.496],\n                          [-163.76, -236.875],\n                          [-208.04, -208.039],\n                          [-236.876, -163.76],\n                          [-248.497, -103.075],\n                          [-250, 0.001],\n                          [-248.497, 103.075],\n                          [-236.876, 163.759],\n                          [-208.04, 208.04],\n                          [-163.76, 236.876],\n                          [-103.075, 248.496],\n                          [0.001, 250],\n                          [103.075, 248.496],\n                          [163.759, 236.876],\n                          [208.04, 208.04],\n                          [236.876, 163.759],\n                          [248.496, 103.075],\n                          [250, 0.001],\n                          [248.496, -103.075],\n                          [236.876, -163.76],\n                          [208.04, -208.039],\n                          [163.759, -236.875],\n                          [103.075, -248.496]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 68\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 2\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [0, 0, 0, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 3\",\n          \"np\": 3,\n          \"cix\": 2,\n          \"ix\": 3,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 71,\n      \"st\": 0,\n      \"bm\": 4\n    }\n  ],\n  \"markers\": []\n}\n"
  },
  {
    "path": "app/assets/lotties/lottieflow-social-networks-15-5-FFFFFF-easey.json",
    "content": "{\n  \"v\": \"5.3.4\",\n  \"fr\": 60,\n  \"ip\": 0,\n  \"op\": 71,\n  \"w\": 584,\n  \"h\": 598,\n  \"nm\": \"inst ver 2\",\n  \"ddd\": 0,\n  \"assets\": [],\n  \"layers\": [\n    {\n      \"ddd\": 0,\n      \"ind\": 1,\n      \"ty\": 4,\n      \"nm\": \"Слой 13\",\n      \"parent\": 3,\n      \"td\": 1,\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 0,\n          \"k\": 0,\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [65.63, 0],\n                    [0, 65.63],\n                    [-65.631, 0],\n                    [0, -65.631]\n                  ],\n                  \"o\": [\n                    [-65.631, 0],\n                    [0, -65.631],\n                    [65.63, 0],\n                    [0, 65.63]\n                  ],\n                  \"v\": [\n                    [0.001, 118.834],\n                    [-118.834, 0.001],\n                    [0.001, -118.834],\n                    [118.834, 0.001]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ind\": 1,\n              \"ty\": \"sh\",\n              \"ix\": 2,\n              \"ks\": {\n                \"a\": 0,\n                \"k\": {\n                  \"i\": [\n                    [1.217, 26.665],\n                    [6.18, 15.902],\n                    [13.898, 13.898],\n                    [16.44, 6.389],\n                    [26.61, 1.215],\n                    [67.895, 0],\n                    [26.665, -1.217],\n                    [15.903, -6.181],\n                    [13.898, -13.898],\n                    [6.389, -16.44],\n                    [1.215, -26.61],\n                    [0, -67.896],\n                    [-1.217, -26.665],\n                    [-6.181, -15.902],\n                    [-13.898, -13.899],\n                    [-16.44, -6.389],\n                    [-26.61, -1.214],\n                    [-67.896, 0],\n                    [-26.665, 1.217],\n                    [-15.902, 6.18],\n                    [-13.899, 13.898],\n                    [-6.389, 16.44],\n                    [-1.214, 26.61],\n                    [0, 67.895]\n                  ],\n                  \"o\": [\n                    [-1.214, -26.61],\n                    [-6.389, -16.44],\n                    [-13.899, -13.898],\n                    [-15.902, -6.181],\n                    [-26.665, -1.217],\n                    [-67.896, 0],\n                    [-26.61, 1.215],\n                    [-16.44, 6.389],\n                    [-13.898, 13.898],\n                    [-6.181, 15.902],\n                    [-1.217, 26.665],\n                    [0, 67.895],\n                    [1.215, 26.61],\n                    [6.389, 16.44],\n                    [13.898, 13.898],\n                    [15.903, 6.18],\n                    [26.665, 1.217],\n                    [67.895, 0],\n                    [26.61, -1.214],\n                    [16.44, -6.389],\n                    [13.898, -13.899],\n                    [6.18, -15.902],\n                    [1.217, -26.665],\n                    [0, -67.896]\n                  ],\n                  \"v\": [\n                    [248.496, -103.075],\n                    [236.876, -163.76],\n                    [208.04, -208.039],\n                    [163.759, -236.875],\n                    [103.075, -248.496],\n                    [0, -250],\n                    [-103.075, -248.496],\n                    [-163.76, -236.875],\n                    [-208.04, -208.039],\n                    [-236.876, -163.76],\n                    [-248.497, -103.075],\n                    [-250, 0],\n                    [-248.497, 103.075],\n                    [-236.876, 163.759],\n                    [-208.04, 208.04],\n                    [-163.76, 236.876],\n                    [-103.075, 248.496],\n                    [0, 250],\n                    [103.075, 248.496],\n                    [163.759, 236.876],\n                    [208.04, 208.04],\n                    [236.876, 163.759],\n                    [248.496, 103.075],\n                    [250, 0]\n                  ],\n                  \"c\": true\n                },\n                \"ix\": 2\n              },\n              \"nm\": \"Path 2\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 3,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 71,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 2,\n      \"ty\": 4,\n      \"nm\": \"Слой 17\",\n      \"parent\": 3,\n      \"tt\": 2,\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": [0.542],\n                \"y\": [0.988]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p542_0p988_0p333_0\"],\n              \"t\": 15,\n              \"s\": [182],\n              \"e\": [-9]\n            },\n            {\n              \"i\": {\n                \"x\": [0.564],\n                \"y\": [1.014]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p564_1p014_0p333_0\"],\n              \"t\": 31,\n              \"s\": [-9],\n              \"e\": [182]\n            },\n            {\n              \"t\": 52\n            }\n          ],\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 0,\n          \"k\": [0, -97.667, 0],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, -83.667, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.542,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p542_1_0p333_0\",\n                    \"t\": 18,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -46.208],\n                          [46.208, 0],\n                          [0, 46.208],\n                          [-46.208, 0]\n                        ],\n                        \"o\": [\n                          [0, 46.208],\n                          [-46.208, 0],\n                          [0, -46.208],\n                          [46.208, 0]\n                        ],\n                        \"v\": [\n                          [83.667, 0],\n                          [0, 83.667],\n                          [-83.667, 0],\n                          [0, -83.667]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -59.67],\n                          [58.836, 0],\n                          [0, 59.67],\n                          [-58.836, 0]\n                        ],\n                        \"o\": [\n                          [0, 59.67],\n                          [-58.836, 0],\n                          [0, -59.67],\n                          [58.836, 0]\n                        ],\n                        \"v\": [\n                          [83.667, 24.376],\n                          [-22.865, 132.418],\n                          [-129.396, 24.376],\n                          [-22.865, -83.667]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.833,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p833_1_0p333_0\",\n                    \"t\": 34,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -59.67],\n                          [58.836, 0],\n                          [0, 59.67],\n                          [-58.836, 0]\n                        ],\n                        \"o\": [\n                          [0, 59.67],\n                          [-58.836, 0],\n                          [0, -59.67],\n                          [58.836, 0]\n                        ],\n                        \"v\": [\n                          [83.667, 24.376],\n                          [-22.865, 132.418],\n                          [-129.396, 24.376],\n                          [-22.865, -83.667]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -46.208],\n                          [46.208, 0],\n                          [0, 46.208],\n                          [-46.208, 0]\n                        ],\n                        \"o\": [\n                          [0, 46.208],\n                          [-46.208, 0],\n                          [0, -46.208],\n                          [46.208, 0]\n                        ],\n                        \"v\": [\n                          [83.667, 0],\n                          [0, 83.667],\n                          [-83.667, 0],\n                          [0, -83.667]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 55\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"st\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 3\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 4\n              },\n              \"w\": {\n                \"a\": 0,\n                \"k\": 1,\n                \"ix\": 5\n              },\n              \"lc\": 1,\n              \"lj\": 1,\n              \"ml\": 10,\n              \"ml2\": {\n                \"a\": 0,\n                \"k\": 10,\n                \"ix\": 8\n              },\n              \"nm\": \"Stroke 1\",\n              \"mn\": \"ADBE Vector Graphic - Stroke\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 3,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 21,\n      \"op\": 42,\n      \"st\": 0,\n      \"bm\": 0\n    },\n    {\n      \"ddd\": 0,\n      \"ind\": 3,\n      \"ty\": 4,\n      \"nm\": \"Слой 16\",\n      \"sr\": 1,\n      \"ks\": {\n        \"o\": {\n          \"a\": 0,\n          \"k\": 100,\n          \"ix\": 11\n        },\n        \"r\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": [0.404],\n                \"y\": [1]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p404_1_0p333_0\"],\n              \"t\": 5,\n              \"s\": [0],\n              \"e\": [-9]\n            },\n            {\n              \"i\": {\n                \"x\": [0.421],\n                \"y\": [1]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p421_1_0p333_0\"],\n              \"t\": 20,\n              \"s\": [-9],\n              \"e\": [8]\n            },\n            {\n              \"i\": {\n                \"x\": [0.479],\n                \"y\": [1]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p479_1_0p333_0\"],\n              \"t\": 36,\n              \"s\": [8],\n              \"e\": [-3]\n            },\n            {\n              \"i\": {\n                \"x\": [0.421],\n                \"y\": [1]\n              },\n              \"o\": {\n                \"x\": [0.333],\n                \"y\": [0]\n              },\n              \"n\": [\"0p421_1_0p333_0\"],\n              \"t\": 57,\n              \"s\": [-3],\n              \"e\": [0]\n            },\n            {\n              \"t\": 73\n            }\n          ],\n          \"ix\": 10\n        },\n        \"p\": {\n          \"a\": 1,\n          \"k\": [\n            {\n              \"i\": {\n                \"x\": 0.404,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p404_1_0p333_0\",\n              \"t\": 6,\n              \"s\": [290, 280, 0],\n              \"e\": [290, 272, 0],\n              \"to\": [0, -1.33333337306976, 0],\n              \"ti\": [0, -5.66666650772095, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.421,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p421_1_0p333_0\",\n              \"t\": 21,\n              \"s\": [290, 272, 0],\n              \"e\": [290, 314, 0],\n              \"to\": [0, 5.66666650772095, 0],\n              \"ti\": [0, 0, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.479,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p479_1_0p333_0\",\n              \"t\": 37,\n              \"s\": [290, 314, 0],\n              \"e\": [290, 272, 0],\n              \"to\": [0, 0, 0],\n              \"ti\": [0, 5.66666650772095, 0]\n            },\n            {\n              \"i\": {\n                \"x\": 0.421,\n                \"y\": 1\n              },\n              \"o\": {\n                \"x\": 0.333,\n                \"y\": 0\n              },\n              \"n\": \"0p421_1_0p333_0\",\n              \"t\": 58,\n              \"s\": [290, 272, 0],\n              \"e\": [290, 280, 0],\n              \"to\": [0, -5.66666650772095, 0],\n              \"ti\": [0, -1.33333337306976, 0]\n            },\n            {\n              \"t\": 74\n            }\n          ],\n          \"ix\": 2\n        },\n        \"a\": {\n          \"a\": 0,\n          \"k\": [0, 0, 0],\n          \"ix\": 1\n        },\n        \"s\": {\n          \"a\": 0,\n          \"k\": [100, 100, 100],\n          \"ix\": 6\n        }\n      },\n      \"ao\": 0,\n      \"shapes\": [\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.479,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p479_1_0p333_0\",\n                    \"t\": 1,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -16.569],\n                          [16.569, 0],\n                          [0, 16.569],\n                          [-16.568, 0]\n                        ],\n                        \"o\": [\n                          [0, 16.569],\n                          [-16.568, 0],\n                          [0, -16.569],\n                          [16.569, 0]\n                        ],\n                        \"v\": [\n                          [163.451, -133.45],\n                          [133.45, -103.451],\n                          [103.451, -133.45],\n                          [133.45, -163.45]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -15.291],\n                          [15.291, 0],\n                          [0, 15.291],\n                          [-15.29, 0]\n                        ],\n                        \"o\": [\n                          [0, 15.291],\n                          [-15.29, 0],\n                          [0, -15.291],\n                          [15.291, 0]\n                        ],\n                        \"v\": [\n                          [150.842, -123.156],\n                          [123.156, -95.47],\n                          [95.47, -123.156],\n                          [123.156, -150.841]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 16,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -15.291],\n                          [15.291, 0],\n                          [0, 15.291],\n                          [-15.29, 0]\n                        ],\n                        \"o\": [\n                          [0, 15.291],\n                          [-15.29, 0],\n                          [0, -15.291],\n                          [15.291, 0]\n                        ],\n                        \"v\": [\n                          [150.842, -123.156],\n                          [123.156, -95.47],\n                          [95.47, -123.156],\n                          [123.156, -150.841]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -18.097],\n                          [18.097, 0],\n                          [0, 18.097],\n                          [-18.096, 0]\n                        ],\n                        \"o\": [\n                          [0, 18.097],\n                          [-18.096, 0],\n                          [0, -18.097],\n                          [18.097, 0]\n                        ],\n                        \"v\": [\n                          [178.526, -145.758],\n                          [145.758, -112.992],\n                          [112.992, -145.758],\n                          [145.758, -178.525]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.533,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p533_1_0p333_0\",\n                    \"t\": 32,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -18.097],\n                          [18.097, 0],\n                          [0, 18.097],\n                          [-18.096, 0]\n                        ],\n                        \"o\": [\n                          [0, 18.097],\n                          [-18.096, 0],\n                          [0, -18.097],\n                          [18.097, 0]\n                        ],\n                        \"v\": [\n                          [178.526, -145.758],\n                          [145.758, -112.992],\n                          [112.992, -145.758],\n                          [145.758, -178.525]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -15.049],\n                          [15.049, 0],\n                          [0, 15.049],\n                          [-15.049, 0]\n                        ],\n                        \"o\": [\n                          [0, 15.049],\n                          [-15.049, 0],\n                          [0, -15.049],\n                          [15.049, 0]\n                        ],\n                        \"v\": [\n                          [148.463, -121.214],\n                          [121.214, -93.965],\n                          [93.965, -121.214],\n                          [121.214, -148.462]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 53,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [0, -15.049],\n                          [15.049, 0],\n                          [0, 15.049],\n                          [-15.049, 0]\n                        ],\n                        \"o\": [\n                          [0, 15.049],\n                          [-15.049, 0],\n                          [0, -15.049],\n                          [15.049, 0]\n                        ],\n                        \"v\": [\n                          [148.463, -121.214],\n                          [121.214, -93.965],\n                          [93.965, -121.214],\n                          [121.214, -148.462]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [0, -16.569],\n                          [16.569, 0],\n                          [0, 16.569],\n                          [-16.568, 0]\n                        ],\n                        \"o\": [\n                          [0, 16.569],\n                          [-16.568, 0],\n                          [0, -16.569],\n                          [16.569, 0]\n                        ],\n                        \"v\": [\n                          [163.451, -133.45],\n                          [133.45, -103.451],\n                          [103.451, -133.45],\n                          [133.45, -163.45]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 69\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 1\",\n          \"np\": 2,\n          \"cix\": 2,\n          \"ix\": 1,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        },\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.479,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p479_1_0p333_0\",\n                    \"t\": 1,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [70.901, 0],\n                          [0, -70.902],\n                          [-70.902, 0],\n                          [0, 70.901]\n                        ],\n                        \"o\": [\n                          [-70.902, 0],\n                          [0, 70.901],\n                          [70.901, 0],\n                          [0, -70.902]\n                        ],\n                        \"v\": [\n                          [0.001, -128.378],\n                          [-128.378, 0.001],\n                          [0.001, 128.378],\n                          [128.378, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [65.432, 0],\n                          [0, -65.433],\n                          [-65.433, 0],\n                          [0, 65.432]\n                        ],\n                        \"o\": [\n                          [-65.433, 0],\n                          [0, 65.432],\n                          [65.432, 0],\n                          [0, -65.433]\n                        ],\n                        \"v\": [\n                          [0, -118.475],\n                          [-118.475, 0],\n                          [0, 118.475],\n                          [118.475, 0]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 16,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [65.432, 0],\n                          [0, -65.433],\n                          [-65.433, 0],\n                          [0, 65.432]\n                        ],\n                        \"o\": [\n                          [-65.433, 0],\n                          [0, 65.432],\n                          [65.432, 0],\n                          [0, -65.433]\n                        ],\n                        \"v\": [\n                          [0, -118.475],\n                          [-118.475, 0],\n                          [0, 118.475],\n                          [118.475, 0]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [77.44, 0],\n                          [0, -77.441],\n                          [-77.442, 0],\n                          [0, 77.44]\n                        ],\n                        \"o\": [\n                          [-77.442, 0],\n                          [0, 77.44],\n                          [77.44, 0],\n                          [0, -77.441]\n                        ],\n                        \"v\": [\n                          [0.001, -140.218],\n                          [-140.218, 0.001],\n                          [0.001, 140.218],\n                          [140.218, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.265,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p265_1_0p333_0\",\n                    \"t\": 32,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [77.44, 0],\n                          [0, -77.441],\n                          [-77.442, 0],\n                          [0, 77.44]\n                        ],\n                        \"o\": [\n                          [-77.442, 0],\n                          [0, 77.44],\n                          [77.44, 0],\n                          [0, -77.441]\n                        ],\n                        \"v\": [\n                          [0.001, -140.218],\n                          [-140.218, 0.001],\n                          [0.001, 140.218],\n                          [140.218, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [70.205, 0],\n                          [0, -70.206],\n                          [-70.206, 0],\n                          [0, 70.205]\n                        ],\n                        \"o\": [\n                          [-70.206, 0],\n                          [0, 70.205],\n                          [70.205, 0],\n                          [0, -70.206]\n                        ],\n                        \"v\": [\n                          [0.001, -127.117],\n                          [-127.117, 0.001],\n                          [0.001, 127.117],\n                          [127.117, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 53,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [70.205, 0],\n                          [0, -70.206],\n                          [-70.206, 0],\n                          [0, 70.205]\n                        ],\n                        \"o\": [\n                          [-70.206, 0],\n                          [0, 70.205],\n                          [70.205, 0],\n                          [0, -70.206]\n                        ],\n                        \"v\": [\n                          [0.001, -127.117],\n                          [-127.117, 0.001],\n                          [0.001, 127.117],\n                          [127.117, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [70.901, 0],\n                          [0, -70.902],\n                          [-70.902, 0],\n                          [0, 70.901]\n                        ],\n                        \"o\": [\n                          [-70.902, 0],\n                          [0, 70.901],\n                          [70.901, 0],\n                          [0, -70.902]\n                        ],\n                        \"v\": [\n                          [0.001, -128.378],\n                          [-128.378, 0.001],\n                          [0.001, 128.378],\n                          [128.378, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 69\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ind\": 1,\n              \"ty\": \"sh\",\n              \"ix\": 2,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.479,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p479_1_0p333_0\",\n                    \"t\": 1,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [46.023, 0],\n                          [0, 46.023],\n                          [-46.024, 0],\n                          [0, -46.024]\n                        ],\n                        \"o\": [\n                          [-46.024, 0],\n                          [0, -46.024],\n                          [46.023, 0],\n                          [0, 46.023]\n                        ],\n                        \"v\": [\n                          [0.001, 83.333],\n                          [-83.333, 0.001],\n                          [0.001, -83.333],\n                          [83.333, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [42.473, 0],\n                          [0, 42.473],\n                          [-42.474, 0],\n                          [0, -42.474]\n                        ],\n                        \"o\": [\n                          [-42.474, 0],\n                          [0, -42.474],\n                          [42.473, 0],\n                          [0, 42.473]\n                        ],\n                        \"v\": [\n                          [0, 76.905],\n                          [-76.905, 0],\n                          [0, -76.905],\n                          [76.905, 0]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 16,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [42.473, 0],\n                          [0, 42.473],\n                          [-42.474, 0],\n                          [0, -42.474]\n                        ],\n                        \"o\": [\n                          [-42.474, 0],\n                          [0, -42.474],\n                          [42.473, 0],\n                          [0, 42.473]\n                        ],\n                        \"v\": [\n                          [0, 76.905],\n                          [-76.905, 0],\n                          [0, -76.905],\n                          [76.905, 0]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [50.268, 0],\n                          [0, 50.268],\n                          [-50.269, 0],\n                          [0, -50.269]\n                        ],\n                        \"o\": [\n                          [-50.269, 0],\n                          [0, -50.269],\n                          [50.268, 0],\n                          [0, 50.268]\n                        ],\n                        \"v\": [\n                          [0.001, 91.019],\n                          [-91.019, 0.001],\n                          [0.001, -91.019],\n                          [91.019, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.265,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p265_1_0p333_0\",\n                    \"t\": 32,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [50.268, 0],\n                          [0, 50.268],\n                          [-50.269, 0],\n                          [0, -50.269]\n                        ],\n                        \"o\": [\n                          [-50.269, 0],\n                          [0, -50.269],\n                          [50.268, 0],\n                          [0, 50.268]\n                        ],\n                        \"v\": [\n                          [0.001, 91.019],\n                          [-91.019, 0.001],\n                          [0.001, -91.019],\n                          [91.019, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [45.571, 0],\n                          [0, 45.571],\n                          [-45.572, 0],\n                          [0, -45.572]\n                        ],\n                        \"o\": [\n                          [-45.572, 0],\n                          [0, -45.572],\n                          [45.571, 0],\n                          [0, 45.571]\n                        ],\n                        \"v\": [\n                          [0, 82.515],\n                          [-82.515, 0],\n                          [0, -82.515],\n                          [82.515, 0]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 53,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [45.571, 0],\n                          [0, 45.571],\n                          [-45.572, 0],\n                          [0, -45.572]\n                        ],\n                        \"o\": [\n                          [-45.572, 0],\n                          [0, -45.572],\n                          [45.571, 0],\n                          [0, 45.571]\n                        ],\n                        \"v\": [\n                          [0, 82.515],\n                          [-82.515, 0],\n                          [0, -82.515],\n                          [82.515, 0]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [46.023, 0],\n                          [0, 46.023],\n                          [-46.024, 0],\n                          [0, -46.024]\n                        ],\n                        \"o\": [\n                          [-46.024, 0],\n                          [0, -46.024],\n                          [46.023, 0],\n                          [0, 46.023]\n                        ],\n                        \"v\": [\n                          [0.001, 83.333],\n                          [-83.333, 0.001],\n                          [0.001, -83.333],\n                          [83.333, 0.001]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 69\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 2\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 2\",\n          \"np\": 3,\n          \"cix\": 2,\n          \"ix\": 2,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        },\n        {\n          \"ty\": \"gr\",\n          \"it\": [\n            {\n              \"ind\": 0,\n              \"ty\": \"sh\",\n              \"ix\": 1,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.479,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p479_1_0p333_0\",\n                    \"t\": 0,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [-66.753, 0],\n                          [-26.361, -1.203],\n                          [-8.809, -3.423],\n                          [-8.748, -8.748],\n                          [-4.535, -11.668],\n                          [-1.111, -24.375],\n                          [0, -66.753],\n                          [1.203, -26.362],\n                          [3.424, -8.809],\n                          [8.749, -8.748],\n                          [11.669, -4.535],\n                          [24.375, -1.111],\n                          [66.757, 0],\n                          [26.359, 1.203],\n                          [8.809, 3.424],\n                          [8.749, 8.749],\n                          [4.534, 11.669],\n                          [1.112, 24.375],\n                          [0, 66.752],\n                          [-1.203, 26.361],\n                          [-3.424, 8.809],\n                          [-8.748, 8.748],\n                          [-11.669, 4.535],\n                          [-24.375, 1.112]\n                        ],\n                        \"o\": [\n                          [66.752, 0],\n                          [24.375, 1.112],\n                          [11.669, 4.535],\n                          [8.749, 8.748],\n                          [3.424, 8.809],\n                          [1.203, 26.361],\n                          [0, 66.752],\n                          [-1.111, 24.375],\n                          [-4.535, 11.669],\n                          [-8.748, 8.749],\n                          [-8.809, 3.424],\n                          [-26.358, 1.203],\n                          [-66.758, 0],\n                          [-24.375, -1.111],\n                          [-11.669, -4.535],\n                          [-8.748, -8.748],\n                          [-3.424, -8.809],\n                          [-1.203, -26.362],\n                          [0, -66.753],\n                          [1.112, -24.375],\n                          [4.534, -11.668],\n                          [8.749, -8.748],\n                          [8.809, -3.423],\n                          [26.361, -1.203]\n                        ],\n                        \"v\": [\n                          [0.001, -204.954],\n                          [101.021, -203.497],\n                          [147.443, -194.889],\n                          [176.188, -176.188],\n                          [194.889, -147.443],\n                          [203.497, -101.021],\n                          [204.955, 0.001],\n                          [203.497, 101.021],\n                          [194.889, 147.443],\n                          [176.188, 176.188],\n                          [147.443, 194.889],\n                          [101.021, 203.497],\n                          [0.001, 204.955],\n                          [-101.021, 203.497],\n                          [-147.443, 194.889],\n                          [-176.189, 176.188],\n                          [-194.889, 147.443],\n                          [-203.498, 101.021],\n                          [-204.955, 0.001],\n                          [-203.498, -101.021],\n                          [-194.889, -147.443],\n                          [-176.189, -176.188],\n                          [-147.443, -194.889],\n                          [-101.021, -203.497]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [-61.604, 0],\n                          [-24.328, -1.11],\n                          [-8.13, -3.159],\n                          [-8.073, -8.072],\n                          [-4.185, -10.768],\n                          [-1.026, -22.494],\n                          [0, -61.604],\n                          [1.109, -24.328],\n                          [3.16, -8.13],\n                          [8.073, -8.074],\n                          [10.769, -4.185],\n                          [22.494, -1.026],\n                          [61.607, 0],\n                          [24.325, 1.109],\n                          [8.13, 3.159],\n                          [8.074, 8.074],\n                          [4.185, 10.769],\n                          [1.026, 22.494],\n                          [0, 61.603],\n                          [-1.11, 24.328],\n                          [-3.159, 8.13],\n                          [-8.073, 8.072],\n                          [-10.77, 4.184],\n                          [-22.494, 1.026]\n                        ],\n                        \"o\": [\n                          [61.603, 0],\n                          [22.494, 1.027],\n                          [10.769, 4.185],\n                          [8.074, 8.073],\n                          [3.16, 8.13],\n                          [1.11, 24.328],\n                          [0, 61.603],\n                          [-1.026, 22.494],\n                          [-4.185, 10.769],\n                          [-8.073, 8.074],\n                          [-8.13, 3.16],\n                          [-24.324, 1.11],\n                          [-61.608, 0],\n                          [-22.494, -1.026],\n                          [-10.769, -4.185],\n                          [-8.073, -8.073],\n                          [-3.16, -8.13],\n                          [-1.11, -24.328],\n                          [0, -61.604],\n                          [1.027, -22.494],\n                          [4.184, -10.768],\n                          [8.074, -8.073],\n                          [8.13, -3.159],\n                          [24.328, -1.11]\n                        ],\n                        \"v\": [\n                          [0, -189.143],\n                          [93.228, -187.799],\n                          [136.069, -179.855],\n                          [162.596, -162.596],\n                          [179.855, -136.069],\n                          [187.799, -93.228],\n                          [189.144, 0],\n                          [187.799, 93.228],\n                          [179.855, 136.069],\n                          [162.596, 162.596],\n                          [136.069, 179.855],\n                          [93.228, 187.799],\n                          [0, 189.144],\n                          [-93.228, 187.799],\n                          [-136.069, 179.855],\n                          [-162.597, 162.596],\n                          [-179.855, 136.069],\n                          [-187.8, 93.228],\n                          [-189.144, 0],\n                          [-187.8, -93.228],\n                          [-179.855, -136.069],\n                          [-162.597, -162.596],\n                          [-136.069, -179.855],\n                          [-93.228, -187.799]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 15,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [-61.604, 0],\n                          [-24.328, -1.11],\n                          [-8.13, -3.159],\n                          [-8.073, -8.072],\n                          [-4.185, -10.768],\n                          [-1.026, -22.494],\n                          [0, -61.604],\n                          [1.109, -24.328],\n                          [3.16, -8.13],\n                          [8.073, -8.074],\n                          [10.769, -4.185],\n                          [22.494, -1.026],\n                          [61.607, 0],\n                          [24.325, 1.109],\n                          [8.13, 3.159],\n                          [8.074, 8.074],\n                          [4.185, 10.769],\n                          [1.026, 22.494],\n                          [0, 61.603],\n                          [-1.11, 24.328],\n                          [-3.159, 8.13],\n                          [-8.073, 8.072],\n                          [-10.77, 4.184],\n                          [-22.494, 1.026]\n                        ],\n                        \"o\": [\n                          [61.603, 0],\n                          [22.494, 1.027],\n                          [10.769, 4.185],\n                          [8.074, 8.073],\n                          [3.16, 8.13],\n                          [1.11, 24.328],\n                          [0, 61.603],\n                          [-1.026, 22.494],\n                          [-4.185, 10.769],\n                          [-8.073, 8.074],\n                          [-8.13, 3.16],\n                          [-24.324, 1.11],\n                          [-61.608, 0],\n                          [-22.494, -1.026],\n                          [-10.769, -4.185],\n                          [-8.073, -8.073],\n                          [-3.16, -8.13],\n                          [-1.11, -24.328],\n                          [0, -61.604],\n                          [1.027, -22.494],\n                          [4.184, -10.768],\n                          [8.074, -8.073],\n                          [8.13, -3.159],\n                          [24.328, -1.11]\n                        ],\n                        \"v\": [\n                          [0, -189.143],\n                          [93.228, -187.799],\n                          [136.069, -179.855],\n                          [162.596, -162.596],\n                          [179.855, -136.069],\n                          [187.799, -93.228],\n                          [189.144, 0],\n                          [187.799, 93.228],\n                          [179.855, 136.069],\n                          [162.596, 162.596],\n                          [136.069, 179.855],\n                          [93.228, 187.799],\n                          [0, 189.144],\n                          [-93.228, 187.799],\n                          [-136.069, 179.855],\n                          [-162.597, 162.596],\n                          [-179.855, 136.069],\n                          [-187.8, 93.228],\n                          [-189.144, 0],\n                          [-187.8, -93.228],\n                          [-179.855, -136.069],\n                          [-162.597, -162.596],\n                          [-136.069, -179.855],\n                          [-93.228, -187.799]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [-72.91, 0],\n                          [-28.793, -1.314],\n                          [-9.622, -3.739],\n                          [-9.555, -9.554],\n                          [-4.953, -12.745],\n                          [-1.215, -26.623],\n                          [0, -72.91],\n                          [1.313, -28.793],\n                          [3.739, -9.622],\n                          [9.555, -9.556],\n                          [12.746, -4.953],\n                          [26.623, -1.215],\n                          [72.914, 0],\n                          [28.79, 1.313],\n                          [9.622, 3.739],\n                          [9.556, 9.556],\n                          [4.953, 12.745],\n                          [1.215, 26.623],\n                          [0, 72.909],\n                          [-1.314, 28.793],\n                          [-3.739, 9.622],\n                          [-9.555, 9.554],\n                          [-12.746, 4.952],\n                          [-26.623, 1.215]\n                        ],\n                        \"o\": [\n                          [72.909, 0],\n                          [26.623, 1.215],\n                          [12.746, 4.953],\n                          [9.556, 9.555],\n                          [3.739, 9.622],\n                          [1.314, 28.793],\n                          [0, 72.909],\n                          [-1.214, 26.623],\n                          [-4.953, 12.746],\n                          [-9.555, 9.556],\n                          [-9.622, 3.74],\n                          [-28.788, 1.314],\n                          [-72.915, 0],\n                          [-26.623, -1.214],\n                          [-12.746, -4.953],\n                          [-9.555, -9.555],\n                          [-3.739, -9.622],\n                          [-1.314, -28.793],\n                          [0, -72.91],\n                          [1.215, -26.623],\n                          [4.952, -12.745],\n                          [9.556, -9.555],\n                          [9.622, -3.738],\n                          [28.793, -1.314]\n                        ],\n                        \"v\": [\n                          [0.001, -223.857],\n                          [110.338, -222.265],\n                          [161.041, -212.864],\n                          [192.438, -192.437],\n                          [212.864, -161.041],\n                          [222.265, -110.338],\n                          [223.858, 0.001],\n                          [222.265, 110.338],\n                          [212.864, 161.041],\n                          [192.438, 192.437],\n                          [161.041, 212.864],\n                          [110.338, 222.265],\n                          [0.001, 223.858],\n                          [-110.338, 222.265],\n                          [-161.041, 212.864],\n                          [-192.439, 192.437],\n                          [-212.864, 161.041],\n                          [-222.266, 110.338],\n                          [-223.858, 0.001],\n                          [-222.266, -110.338],\n                          [-212.864, -161.041],\n                          [-192.439, -192.437],\n                          [-161.041, -212.864],\n                          [-110.338, -222.265]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.533,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p533_1_0p333_0\",\n                    \"t\": 31,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [-72.91, 0],\n                          [-28.793, -1.314],\n                          [-9.622, -3.739],\n                          [-9.555, -9.554],\n                          [-4.953, -12.745],\n                          [-1.215, -26.623],\n                          [0, -72.91],\n                          [1.313, -28.793],\n                          [3.739, -9.622],\n                          [9.555, -9.556],\n                          [12.746, -4.953],\n                          [26.623, -1.215],\n                          [72.914, 0],\n                          [28.79, 1.313],\n                          [9.622, 3.739],\n                          [9.556, 9.556],\n                          [4.953, 12.745],\n                          [1.215, 26.623],\n                          [0, 72.909],\n                          [-1.314, 28.793],\n                          [-3.739, 9.622],\n                          [-9.555, 9.554],\n                          [-12.746, 4.952],\n                          [-26.623, 1.215]\n                        ],\n                        \"o\": [\n                          [72.909, 0],\n                          [26.623, 1.215],\n                          [12.746, 4.953],\n                          [9.556, 9.555],\n                          [3.739, 9.622],\n                          [1.314, 28.793],\n                          [0, 72.909],\n                          [-1.214, 26.623],\n                          [-4.953, 12.746],\n                          [-9.555, 9.556],\n                          [-9.622, 3.74],\n                          [-28.788, 1.314],\n                          [-72.915, 0],\n                          [-26.623, -1.214],\n                          [-12.746, -4.953],\n                          [-9.555, -9.555],\n                          [-3.739, -9.622],\n                          [-1.314, -28.793],\n                          [0, -72.91],\n                          [1.215, -26.623],\n                          [4.952, -12.745],\n                          [9.556, -9.555],\n                          [9.622, -3.738],\n                          [28.793, -1.314]\n                        ],\n                        \"v\": [\n                          [0.001, -223.857],\n                          [110.338, -222.265],\n                          [161.041, -212.864],\n                          [192.438, -192.437],\n                          [212.864, -161.041],\n                          [222.265, -110.338],\n                          [223.858, 0.001],\n                          [222.265, 110.338],\n                          [212.864, 161.041],\n                          [192.438, 192.437],\n                          [161.041, 212.864],\n                          [110.338, 222.265],\n                          [0.001, 223.858],\n                          [-110.338, 222.265],\n                          [-161.041, 212.864],\n                          [-192.439, 192.437],\n                          [-212.864, 161.041],\n                          [-222.266, 110.338],\n                          [-223.858, 0.001],\n                          [-222.266, -110.338],\n                          [-212.864, -161.041],\n                          [-192.439, -192.437],\n                          [-161.041, -212.864],\n                          [-110.338, -222.265]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [-60.632, 0],\n                          [-23.944, -1.093],\n                          [-8.001, -3.11],\n                          [-7.946, -7.945],\n                          [-4.119, -10.599],\n                          [-1.01, -22.14],\n                          [0, -60.632],\n                          [1.092, -23.944],\n                          [3.11, -8.002],\n                          [7.946, -7.947],\n                          [10.599, -4.119],\n                          [22.14, -1.01],\n                          [60.636, 0],\n                          [23.942, 1.092],\n                          [8.002, 3.11],\n                          [7.947, 7.947],\n                          [4.119, 10.599],\n                          [1.01, 22.14],\n                          [0, 60.631],\n                          [-1.093, 23.944],\n                          [-3.109, 8.002],\n                          [-7.946, 7.945],\n                          [-10.6, 4.118],\n                          [-22.14, 1.01]\n                        ],\n                        \"o\": [\n                          [60.631, 0],\n                          [22.14, 1.01],\n                          [10.599, 4.119],\n                          [7.947, 7.946],\n                          [3.11, 8.002],\n                          [1.092, 23.944],\n                          [0, 60.631],\n                          [-1.01, 22.14],\n                          [-4.119, 10.599],\n                          [-7.946, 7.947],\n                          [-8.002, 3.11],\n                          [-23.941, 1.092],\n                          [-60.637, 0],\n                          [-22.14, -1.01],\n                          [-10.599, -4.119],\n                          [-7.946, -7.946],\n                          [-3.11, -8.002],\n                          [-1.092, -23.944],\n                          [0, -60.632],\n                          [1.01, -22.14],\n                          [4.118, -10.598],\n                          [7.947, -7.946],\n                          [8.002, -3.109],\n                          [23.944, -1.092]\n                        ],\n                        \"v\": [\n                          [0, -186.161],\n                          [91.758, -184.838],\n                          [133.923, -177.019],\n                          [160.033, -160.033],\n                          [177.019, -133.923],\n                          [184.838, -91.758],\n                          [186.162, 0],\n                          [184.838, 91.758],\n                          [177.019, 133.923],\n                          [160.033, 160.033],\n                          [133.923, 177.019],\n                          [91.758, 184.838],\n                          [0, 186.162],\n                          [-91.758, 184.838],\n                          [-133.923, 177.019],\n                          [-160.033, 160.033],\n                          [-177.019, 133.923],\n                          [-184.839, 91.758],\n                          [-186.162, 0],\n                          [-184.839, -91.758],\n                          [-177.019, -133.923],\n                          [-160.033, -160.033],\n                          [-133.923, -177.019],\n                          [-91.758, -184.838]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 52,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [-60.632, 0],\n                          [-23.944, -1.093],\n                          [-8.001, -3.11],\n                          [-7.946, -7.945],\n                          [-4.119, -10.599],\n                          [-1.01, -22.14],\n                          [0, -60.632],\n                          [1.092, -23.944],\n                          [3.11, -8.002],\n                          [7.946, -7.947],\n                          [10.599, -4.119],\n                          [22.14, -1.01],\n                          [60.636, 0],\n                          [23.942, 1.092],\n                          [8.002, 3.11],\n                          [7.947, 7.947],\n                          [4.119, 10.599],\n                          [1.01, 22.14],\n                          [0, 60.631],\n                          [-1.093, 23.944],\n                          [-3.109, 8.002],\n                          [-7.946, 7.945],\n                          [-10.6, 4.118],\n                          [-22.14, 1.01]\n                        ],\n                        \"o\": [\n                          [60.631, 0],\n                          [22.14, 1.01],\n                          [10.599, 4.119],\n                          [7.947, 7.946],\n                          [3.11, 8.002],\n                          [1.092, 23.944],\n                          [0, 60.631],\n                          [-1.01, 22.14],\n                          [-4.119, 10.599],\n                          [-7.946, 7.947],\n                          [-8.002, 3.11],\n                          [-23.941, 1.092],\n                          [-60.637, 0],\n                          [-22.14, -1.01],\n                          [-10.599, -4.119],\n                          [-7.946, -7.946],\n                          [-3.11, -8.002],\n                          [-1.092, -23.944],\n                          [0, -60.632],\n                          [1.01, -22.14],\n                          [4.118, -10.598],\n                          [7.947, -7.946],\n                          [8.002, -3.109],\n                          [23.944, -1.092]\n                        ],\n                        \"v\": [\n                          [0, -186.161],\n                          [91.758, -184.838],\n                          [133.923, -177.019],\n                          [160.033, -160.033],\n                          [177.019, -133.923],\n                          [184.838, -91.758],\n                          [186.162, 0],\n                          [184.838, 91.758],\n                          [177.019, 133.923],\n                          [160.033, 160.033],\n                          [133.923, 177.019],\n                          [91.758, 184.838],\n                          [0, 186.162],\n                          [-91.758, 184.838],\n                          [-133.923, 177.019],\n                          [-160.033, 160.033],\n                          [-177.019, 133.923],\n                          [-184.839, 91.758],\n                          [-186.162, 0],\n                          [-184.839, -91.758],\n                          [-177.019, -133.923],\n                          [-160.033, -160.033],\n                          [-133.923, -177.019],\n                          [-91.758, -184.838]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [-66.753, 0],\n                          [-26.361, -1.203],\n                          [-8.809, -3.423],\n                          [-8.748, -8.748],\n                          [-4.535, -11.668],\n                          [-1.111, -24.375],\n                          [0, -66.753],\n                          [1.203, -26.362],\n                          [3.424, -8.809],\n                          [8.749, -8.748],\n                          [11.669, -4.535],\n                          [24.375, -1.111],\n                          [66.757, 0],\n                          [26.359, 1.203],\n                          [8.809, 3.424],\n                          [8.749, 8.749],\n                          [4.534, 11.669],\n                          [1.112, 24.375],\n                          [0, 66.752],\n                          [-1.203, 26.361],\n                          [-3.424, 8.809],\n                          [-8.748, 8.748],\n                          [-11.669, 4.535],\n                          [-24.375, 1.112]\n                        ],\n                        \"o\": [\n                          [66.752, 0],\n                          [24.375, 1.112],\n                          [11.669, 4.535],\n                          [8.749, 8.748],\n                          [3.424, 8.809],\n                          [1.203, 26.361],\n                          [0, 66.752],\n                          [-1.111, 24.375],\n                          [-4.535, 11.669],\n                          [-8.748, 8.749],\n                          [-8.809, 3.424],\n                          [-26.358, 1.203],\n                          [-66.758, 0],\n                          [-24.375, -1.111],\n                          [-11.669, -4.535],\n                          [-8.748, -8.748],\n                          [-3.424, -8.809],\n                          [-1.203, -26.362],\n                          [0, -66.753],\n                          [1.112, -24.375],\n                          [4.534, -11.668],\n                          [8.749, -8.748],\n                          [8.809, -3.423],\n                          [26.361, -1.203]\n                        ],\n                        \"v\": [\n                          [0.001, -204.954],\n                          [101.021, -203.497],\n                          [147.443, -194.889],\n                          [176.188, -176.188],\n                          [194.889, -147.443],\n                          [203.497, -101.021],\n                          [204.955, 0.001],\n                          [203.497, 101.021],\n                          [194.889, 147.443],\n                          [176.188, 176.188],\n                          [147.443, 194.889],\n                          [101.021, 203.497],\n                          [0.001, 204.955],\n                          [-101.021, 203.497],\n                          [-147.443, 194.889],\n                          [-176.189, 176.188],\n                          [-194.889, 147.443],\n                          [-203.498, 101.021],\n                          [-204.955, 0.001],\n                          [-203.498, -101.021],\n                          [-194.889, -147.443],\n                          [-176.189, -176.188],\n                          [-147.443, -194.889],\n                          [-101.021, -203.497]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 68\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 1\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ind\": 1,\n              \"ty\": \"sh\",\n              \"ix\": 2,\n              \"ks\": {\n                \"a\": 1,\n                \"k\": [\n                  {\n                    \"i\": {\n                      \"x\": 0.479,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p479_1_0p333_0\",\n                    \"t\": 0,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [67.895, 0],\n                          [26.665, -1.217],\n                          [15.903, -6.181],\n                          [13.898, -13.898],\n                          [6.389, -16.44],\n                          [1.215, -26.61],\n                          [0, -67.896],\n                          [-1.217, -26.665],\n                          [-6.181, -15.902],\n                          [-13.898, -13.899],\n                          [-16.44, -6.389],\n                          [-26.61, -1.214],\n                          [-67.896, 0],\n                          [-26.665, 1.217],\n                          [-15.902, 6.18],\n                          [-13.899, 13.898],\n                          [-6.389, 16.44],\n                          [-1.214, 26.61],\n                          [0, 67.895],\n                          [1.217, 26.665],\n                          [6.18, 15.902],\n                          [13.898, 13.898],\n                          [16.44, 6.389],\n                          [26.61, 1.215]\n                        ],\n                        \"o\": [\n                          [-67.896, 0],\n                          [-26.61, 1.215],\n                          [-16.44, 6.389],\n                          [-13.898, 13.898],\n                          [-6.181, 15.902],\n                          [-1.217, 26.665],\n                          [0, 67.895],\n                          [1.215, 26.61],\n                          [6.389, 16.44],\n                          [13.898, 13.898],\n                          [15.903, 6.18],\n                          [26.665, 1.217],\n                          [67.895, 0],\n                          [26.61, -1.214],\n                          [16.44, -6.389],\n                          [13.898, -13.899],\n                          [6.18, -15.902],\n                          [1.217, -26.665],\n                          [0, -67.896],\n                          [-1.214, -26.61],\n                          [-6.389, -16.44],\n                          [-13.899, -13.898],\n                          [-15.902, -6.181],\n                          [-26.665, -1.217]\n                        ],\n                        \"v\": [\n                          [0.001, -250],\n                          [-103.075, -248.496],\n                          [-163.76, -236.875],\n                          [-208.04, -208.039],\n                          [-236.876, -163.76],\n                          [-248.497, -103.075],\n                          [-250, 0.001],\n                          [-248.497, 103.075],\n                          [-236.876, 163.759],\n                          [-208.04, 208.04],\n                          [-163.76, 236.876],\n                          [-103.075, 248.496],\n                          [0.001, 250],\n                          [103.075, 248.496],\n                          [163.759, 236.876],\n                          [208.04, 208.04],\n                          [236.876, 163.759],\n                          [248.496, 103.075],\n                          [250, 0.001],\n                          [248.496, -103.075],\n                          [236.876, -163.76],\n                          [208.04, -208.039],\n                          [163.759, -236.875],\n                          [103.075, -248.496]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [62.658, 0],\n                          [24.608, -1.123],\n                          [14.676, -5.704],\n                          [12.826, -12.826],\n                          [5.897, -15.171],\n                          [1.12, -24.557],\n                          [0, -62.659],\n                          [-1.123, -24.608],\n                          [-5.703, -14.675],\n                          [-12.827, -12.827],\n                          [-15.172, -5.895],\n                          [-24.557, -1.12],\n                          [-62.659, 0],\n                          [-24.608, 1.122],\n                          [-14.675, 5.703],\n                          [-12.826, 12.827],\n                          [-5.896, 15.172],\n                          [-1.12, 24.557],\n                          [0, 62.658],\n                          [1.122, 24.608],\n                          [5.703, 14.675],\n                          [12.827, 12.826],\n                          [15.171, 5.897],\n                          [24.557, 1.12]\n                        ],\n                        \"o\": [\n                          [-62.659, 0],\n                          [-24.557, 1.121],\n                          [-15.172, 5.896],\n                          [-12.826, 12.826],\n                          [-5.704, 14.675],\n                          [-1.123, 24.608],\n                          [0, 62.658],\n                          [1.121, 24.557],\n                          [5.896, 15.172],\n                          [12.826, 12.826],\n                          [14.676, 5.703],\n                          [24.608, 1.123],\n                          [62.658, 0],\n                          [24.557, -1.12],\n                          [15.172, -5.896],\n                          [12.826, -12.827],\n                          [5.703, -14.675],\n                          [1.123, -24.608],\n                          [0, -62.659],\n                          [-1.12, -24.557],\n                          [-5.896, -15.171],\n                          [-12.827, -12.826],\n                          [-14.675, -5.704],\n                          [-24.608, -1.123]\n                        ],\n                        \"v\": [\n                          [0, -230.714],\n                          [-95.123, -229.326],\n                          [-151.128, -218.602],\n                          [-191.992, -191.991],\n                          [-218.603, -151.127],\n                          [-229.327, -95.123],\n                          [-230.714, 0],\n                          [-229.327, 95.123],\n                          [-218.603, 151.127],\n                          [-191.992, 191.992],\n                          [-151.128, 218.603],\n                          [-95.123, 229.326],\n                          [0, 230.714],\n                          [95.123, 229.326],\n                          [151.127, 218.603],\n                          [191.992, 191.992],\n                          [218.603, 151.127],\n                          [229.326, 95.123],\n                          [230.714, 0],\n                          [229.326, -95.123],\n                          [218.603, -151.127],\n                          [191.992, -191.991],\n                          [151.127, -218.602],\n                          [95.123, -229.326]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 15,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [62.658, 0],\n                          [24.608, -1.123],\n                          [14.676, -5.704],\n                          [12.826, -12.826],\n                          [5.897, -15.171],\n                          [1.12, -24.557],\n                          [0, -62.659],\n                          [-1.123, -24.608],\n                          [-5.703, -14.675],\n                          [-12.827, -12.827],\n                          [-15.172, -5.895],\n                          [-24.557, -1.12],\n                          [-62.659, 0],\n                          [-24.608, 1.122],\n                          [-14.675, 5.703],\n                          [-12.826, 12.827],\n                          [-5.896, 15.172],\n                          [-1.12, 24.557],\n                          [0, 62.658],\n                          [1.122, 24.608],\n                          [5.703, 14.675],\n                          [12.827, 12.826],\n                          [15.171, 5.897],\n                          [24.557, 1.12]\n                        ],\n                        \"o\": [\n                          [-62.659, 0],\n                          [-24.557, 1.121],\n                          [-15.172, 5.896],\n                          [-12.826, 12.826],\n                          [-5.704, 14.675],\n                          [-1.123, 24.608],\n                          [0, 62.658],\n                          [1.121, 24.557],\n                          [5.896, 15.172],\n                          [12.826, 12.826],\n                          [14.676, 5.703],\n                          [24.608, 1.123],\n                          [62.658, 0],\n                          [24.557, -1.12],\n                          [15.172, -5.896],\n                          [12.826, -12.827],\n                          [5.703, -14.675],\n                          [1.123, -24.608],\n                          [0, -62.659],\n                          [-1.12, -24.557],\n                          [-5.896, -15.171],\n                          [-12.827, -12.826],\n                          [-14.675, -5.704],\n                          [-24.608, -1.123]\n                        ],\n                        \"v\": [\n                          [0, -230.714],\n                          [-95.123, -229.326],\n                          [-151.128, -218.602],\n                          [-191.992, -191.991],\n                          [-218.603, -151.127],\n                          [-229.327, -95.123],\n                          [-230.714, 0],\n                          [-229.327, 95.123],\n                          [-218.603, 151.127],\n                          [-191.992, 191.992],\n                          [-151.128, 218.603],\n                          [-95.123, 229.326],\n                          [0, 230.714],\n                          [95.123, 229.326],\n                          [151.127, 218.603],\n                          [191.992, 191.992],\n                          [218.603, 151.127],\n                          [229.326, 95.123],\n                          [230.714, 0],\n                          [229.326, -95.123],\n                          [218.603, -151.127],\n                          [191.992, -191.991],\n                          [151.127, -218.602],\n                          [95.123, -229.326]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [74.157, 0],\n                          [29.124, -1.329],\n                          [17.37, -6.75],\n                          [15.18, -15.18],\n                          [6.979, -17.956],\n                          [1.326, -29.064],\n                          [0, -74.158],\n                          [-1.329, -29.124],\n                          [-6.75, -17.369],\n                          [-15.181, -15.181],\n                          [-17.956, -6.977],\n                          [-29.064, -1.326],\n                          [-74.158, 0],\n                          [-29.124, 1.328],\n                          [-17.368, 6.75],\n                          [-15.18, 15.181],\n                          [-6.978, 17.956],\n                          [-1.326, 29.064],\n                          [0, 74.157],\n                          [1.328, 29.124],\n                          [6.75, 17.368],\n                          [15.181, 15.179],\n                          [17.956, 6.979],\n                          [29.064, 1.326]\n                        ],\n                        \"o\": [\n                          [-74.158, 0],\n                          [-29.064, 1.327],\n                          [-17.956, 6.978],\n                          [-15.18, 15.18],\n                          [-6.751, 17.368],\n                          [-1.329, 29.124],\n                          [0, 74.157],\n                          [1.327, 29.064],\n                          [6.978, 17.956],\n                          [15.18, 15.18],\n                          [17.37, 6.749],\n                          [29.124, 1.329],\n                          [74.157, 0],\n                          [29.064, -1.326],\n                          [17.956, -6.978],\n                          [15.18, -15.181],\n                          [6.75, -17.368],\n                          [1.329, -29.124],\n                          [0, -74.158],\n                          [-1.326, -29.064],\n                          [-6.978, -17.956],\n                          [-15.181, -15.18],\n                          [-17.368, -6.751],\n                          [-29.124, -1.329]\n                        ],\n                        \"v\": [\n                          [0.001, -273.057],\n                          [-112.581, -271.414],\n                          [-178.864, -258.721],\n                          [-227.227, -227.226],\n                          [-258.722, -178.863],\n                          [-271.415, -112.581],\n                          [-273.057, 0.001],\n                          [-271.415, 112.581],\n                          [-258.722, 178.863],\n                          [-227.227, 227.227],\n                          [-178.864, 258.722],\n                          [-112.581, 271.414],\n                          [0.001, 273.057],\n                          [112.581, 271.414],\n                          [178.863, 258.722],\n                          [227.228, 227.227],\n                          [258.722, 178.863],\n                          [271.414, 112.581],\n                          [273.057, 0.001],\n                          [271.414, -112.581],\n                          [258.722, -178.863],\n                          [227.228, -227.226],\n                          [178.863, -258.721],\n                          [112.581, -271.414]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.533,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p533_1_0p333_0\",\n                    \"t\": 31,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [74.157, 0],\n                          [29.124, -1.329],\n                          [17.37, -6.75],\n                          [15.18, -15.18],\n                          [6.979, -17.956],\n                          [1.326, -29.064],\n                          [0, -74.158],\n                          [-1.329, -29.124],\n                          [-6.75, -17.369],\n                          [-15.181, -15.181],\n                          [-17.956, -6.977],\n                          [-29.064, -1.326],\n                          [-74.158, 0],\n                          [-29.124, 1.328],\n                          [-17.368, 6.75],\n                          [-15.18, 15.181],\n                          [-6.978, 17.956],\n                          [-1.326, 29.064],\n                          [0, 74.157],\n                          [1.328, 29.124],\n                          [6.75, 17.368],\n                          [15.181, 15.179],\n                          [17.956, 6.979],\n                          [29.064, 1.326]\n                        ],\n                        \"o\": [\n                          [-74.158, 0],\n                          [-29.064, 1.327],\n                          [-17.956, 6.978],\n                          [-15.18, 15.18],\n                          [-6.751, 17.368],\n                          [-1.329, 29.124],\n                          [0, 74.157],\n                          [1.327, 29.064],\n                          [6.978, 17.956],\n                          [15.18, 15.18],\n                          [17.37, 6.749],\n                          [29.124, 1.329],\n                          [74.157, 0],\n                          [29.064, -1.326],\n                          [17.956, -6.978],\n                          [15.18, -15.181],\n                          [6.75, -17.368],\n                          [1.329, -29.124],\n                          [0, -74.158],\n                          [-1.326, -29.064],\n                          [-6.978, -17.956],\n                          [-15.181, -15.18],\n                          [-17.368, -6.751],\n                          [-29.124, -1.329]\n                        ],\n                        \"v\": [\n                          [0.001, -273.057],\n                          [-112.581, -271.414],\n                          [-178.864, -258.721],\n                          [-227.227, -227.226],\n                          [-258.722, -178.863],\n                          [-271.415, -112.581],\n                          [-273.057, 0.001],\n                          [-271.415, 112.581],\n                          [-258.722, 178.863],\n                          [-227.227, 227.227],\n                          [-178.864, 258.722],\n                          [-112.581, 271.414],\n                          [0.001, 273.057],\n                          [112.581, 271.414],\n                          [178.863, 258.722],\n                          [227.228, 227.227],\n                          [258.722, 178.863],\n                          [271.414, 112.581],\n                          [273.057, 0.001],\n                          [271.414, -112.581],\n                          [258.722, -178.863],\n                          [227.228, -227.226],\n                          [178.863, -258.721],\n                          [112.581, -271.414]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [61.67, 0],\n                          [24.22, -1.106],\n                          [14.445, -5.614],\n                          [12.624, -12.624],\n                          [5.804, -14.932],\n                          [1.103, -24.17],\n                          [0, -61.671],\n                          [-1.106, -24.22],\n                          [-5.613, -14.444],\n                          [-12.624, -12.624],\n                          [-14.933, -5.802],\n                          [-24.17, -1.103],\n                          [-61.671, 0],\n                          [-24.22, 1.105],\n                          [-14.444, 5.613],\n                          [-12.624, 12.625],\n                          [-5.803, 14.932],\n                          [-1.103, 24.17],\n                          [0, 61.67],\n                          [1.105, 24.22],\n                          [5.613, 14.444],\n                          [12.624, 12.623],\n                          [14.932, 5.804],\n                          [24.17, 1.103]\n                        ],\n                        \"o\": [\n                          [-61.671, 0],\n                          [-24.17, 1.103],\n                          [-14.932, 5.803],\n                          [-12.624, 12.624],\n                          [-5.614, 14.444],\n                          [-1.105, 24.22],\n                          [0, 61.67],\n                          [1.103, 24.17],\n                          [5.803, 14.932],\n                          [12.624, 12.624],\n                          [14.445, 5.613],\n                          [24.22, 1.105],\n                          [61.67, 0],\n                          [24.17, -1.102],\n                          [14.932, -5.803],\n                          [12.624, -12.625],\n                          [5.613, -14.444],\n                          [1.105, -24.22],\n                          [0, -61.671],\n                          [-1.102, -24.17],\n                          [-5.803, -14.932],\n                          [-12.625, -12.624],\n                          [-14.444, -5.614],\n                          [-24.22, -1.105]\n                        ],\n                        \"v\": [\n                          [0, -227.076],\n                          [-93.623, -225.71],\n                          [-148.745, -215.155],\n                          [-188.964, -188.963],\n                          [-215.156, -148.744],\n                          [-225.711, -93.623],\n                          [-227.076, 0],\n                          [-225.711, 93.623],\n                          [-215.156, 148.744],\n                          [-188.964, 188.964],\n                          [-148.745, 215.156],\n                          [-93.623, 225.71],\n                          [0, 227.076],\n                          [93.623, 225.71],\n                          [148.744, 215.156],\n                          [188.964, 188.964],\n                          [215.156, 148.744],\n                          [225.71, 93.623],\n                          [227.076, 0],\n                          [225.71, -93.623],\n                          [215.156, -148.744],\n                          [188.964, -188.963],\n                          [148.744, -215.155],\n                          [93.623, -225.71]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"i\": {\n                      \"x\": 0.491,\n                      \"y\": 1\n                    },\n                    \"o\": {\n                      \"x\": 0.333,\n                      \"y\": 0\n                    },\n                    \"n\": \"0p491_1_0p333_0\",\n                    \"t\": 52,\n                    \"s\": [\n                      {\n                        \"i\": [\n                          [61.67, 0],\n                          [24.22, -1.106],\n                          [14.445, -5.614],\n                          [12.624, -12.624],\n                          [5.804, -14.932],\n                          [1.103, -24.17],\n                          [0, -61.671],\n                          [-1.106, -24.22],\n                          [-5.613, -14.444],\n                          [-12.624, -12.624],\n                          [-14.933, -5.802],\n                          [-24.17, -1.103],\n                          [-61.671, 0],\n                          [-24.22, 1.105],\n                          [-14.444, 5.613],\n                          [-12.624, 12.625],\n                          [-5.803, 14.932],\n                          [-1.103, 24.17],\n                          [0, 61.67],\n                          [1.105, 24.22],\n                          [5.613, 14.444],\n                          [12.624, 12.623],\n                          [14.932, 5.804],\n                          [24.17, 1.103]\n                        ],\n                        \"o\": [\n                          [-61.671, 0],\n                          [-24.17, 1.103],\n                          [-14.932, 5.803],\n                          [-12.624, 12.624],\n                          [-5.614, 14.444],\n                          [-1.105, 24.22],\n                          [0, 61.67],\n                          [1.103, 24.17],\n                          [5.803, 14.932],\n                          [12.624, 12.624],\n                          [14.445, 5.613],\n                          [24.22, 1.105],\n                          [61.67, 0],\n                          [24.17, -1.102],\n                          [14.932, -5.803],\n                          [12.624, -12.625],\n                          [5.613, -14.444],\n                          [1.105, -24.22],\n                          [0, -61.671],\n                          [-1.102, -24.17],\n                          [-5.803, -14.932],\n                          [-12.625, -12.624],\n                          [-14.444, -5.614],\n                          [-24.22, -1.105]\n                        ],\n                        \"v\": [\n                          [0, -227.076],\n                          [-93.623, -225.71],\n                          [-148.745, -215.155],\n                          [-188.964, -188.963],\n                          [-215.156, -148.744],\n                          [-225.711, -93.623],\n                          [-227.076, 0],\n                          [-225.711, 93.623],\n                          [-215.156, 148.744],\n                          [-188.964, 188.964],\n                          [-148.745, 215.156],\n                          [-93.623, 225.71],\n                          [0, 227.076],\n                          [93.623, 225.71],\n                          [148.744, 215.156],\n                          [188.964, 188.964],\n                          [215.156, 148.744],\n                          [225.71, 93.623],\n                          [227.076, 0],\n                          [225.71, -93.623],\n                          [215.156, -148.744],\n                          [188.964, -188.963],\n                          [148.744, -215.155],\n                          [93.623, -225.71]\n                        ],\n                        \"c\": true\n                      }\n                    ],\n                    \"e\": [\n                      {\n                        \"i\": [\n                          [67.895, 0],\n                          [26.665, -1.217],\n                          [15.903, -6.181],\n                          [13.898, -13.898],\n                          [6.389, -16.44],\n                          [1.215, -26.61],\n                          [0, -67.896],\n                          [-1.217, -26.665],\n                          [-6.181, -15.902],\n                          [-13.898, -13.899],\n                          [-16.44, -6.389],\n                          [-26.61, -1.214],\n                          [-67.896, 0],\n                          [-26.665, 1.217],\n                          [-15.902, 6.18],\n                          [-13.899, 13.898],\n                          [-6.389, 16.44],\n                          [-1.214, 26.61],\n                          [0, 67.895],\n                          [1.217, 26.665],\n                          [6.18, 15.902],\n                          [13.898, 13.898],\n                          [16.44, 6.389],\n                          [26.61, 1.215]\n                        ],\n                        \"o\": [\n                          [-67.896, 0],\n                          [-26.61, 1.215],\n                          [-16.44, 6.389],\n                          [-13.898, 13.898],\n                          [-6.181, 15.902],\n                          [-1.217, 26.665],\n                          [0, 67.895],\n                          [1.215, 26.61],\n                          [6.389, 16.44],\n                          [13.898, 13.898],\n                          [15.903, 6.18],\n                          [26.665, 1.217],\n                          [67.895, 0],\n                          [26.61, -1.214],\n                          [16.44, -6.389],\n                          [13.898, -13.899],\n                          [6.18, -15.902],\n                          [1.217, -26.665],\n                          [0, -67.896],\n                          [-1.214, -26.61],\n                          [-6.389, -16.44],\n                          [-13.899, -13.898],\n                          [-15.902, -6.181],\n                          [-26.665, -1.217]\n                        ],\n                        \"v\": [\n                          [0.001, -250],\n                          [-103.075, -248.496],\n                          [-163.76, -236.875],\n                          [-208.04, -208.039],\n                          [-236.876, -163.76],\n                          [-248.497, -103.075],\n                          [-250, 0.001],\n                          [-248.497, 103.075],\n                          [-236.876, 163.759],\n                          [-208.04, 208.04],\n                          [-163.76, 236.876],\n                          [-103.075, 248.496],\n                          [0.001, 250],\n                          [103.075, 248.496],\n                          [163.759, 236.876],\n                          [208.04, 208.04],\n                          [236.876, 163.759],\n                          [248.496, 103.075],\n                          [250, 0.001],\n                          [248.496, -103.075],\n                          [236.876, -163.76],\n                          [208.04, -208.039],\n                          [163.759, -236.875],\n                          [103.075, -248.496]\n                        ],\n                        \"c\": true\n                      }\n                    ]\n                  },\n                  {\n                    \"t\": 68\n                  }\n                ],\n                \"ix\": 2\n              },\n              \"nm\": \"Path 2\",\n              \"mn\": \"ADBE Vector Shape - Group\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"fl\",\n              \"c\": {\n                \"a\": 0,\n                \"k\": [1, 1, 1, 1],\n                \"ix\": 4\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 5\n              },\n              \"r\": 1,\n              \"nm\": \"Fill 1\",\n              \"mn\": \"ADBE Vector Graphic - Fill\",\n              \"hd\": false\n            },\n            {\n              \"ty\": \"tr\",\n              \"p\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 2\n              },\n              \"a\": {\n                \"a\": 0,\n                \"k\": [0, 0],\n                \"ix\": 1\n              },\n              \"s\": {\n                \"a\": 0,\n                \"k\": [100, 100],\n                \"ix\": 3\n              },\n              \"r\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 6\n              },\n              \"o\": {\n                \"a\": 0,\n                \"k\": 100,\n                \"ix\": 7\n              },\n              \"sk\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 4\n              },\n              \"sa\": {\n                \"a\": 0,\n                \"k\": 0,\n                \"ix\": 5\n              },\n              \"nm\": \"Transform\"\n            }\n          ],\n          \"nm\": \"Group 3\",\n          \"np\": 3,\n          \"cix\": 2,\n          \"ix\": 3,\n          \"mn\": \"ADBE Vector Group\",\n          \"hd\": false\n        }\n      ],\n      \"ip\": 0,\n      \"op\": 71,\n      \"st\": 0,\n      \"bm\": 4\n    }\n  ],\n  \"markers\": []\n}\n"
  },
  {
    "path": "app/components/elements/AspectRatio.tsx",
    "content": "import * as AspectRatioPrimitive from '@radix-ui/react-aspect-ratio';\n\nconst AspectRatio = AspectRatioPrimitive.Root;\n\nexport default AspectRatio;\n"
  },
  {
    "path": "app/components/elements/Breadcrumb.tsx",
    "content": "import * as React from 'react';\nimport { NavLink } from '@remix-run/react';\nimport { cn } from '~/utils';\n\nimport ChevronRight from '~/assets/icons/ChevronRightIcon';\n\nfunction getValidChildren(children: React.ReactNode) {\n  return React.Children.toArray(children).filter((child) =>\n    React.isValidElement(child),\n  ) as React.ReactElement[];\n}\n\nexport interface BreadcrumbProps extends React.ComponentPropsWithoutRef<'nav'> {\n  /* The visual separator between each breadcrumb item */\n  separator?: React.ReactNode;\n  /**\n   * If `true`, adds a separator between each breadcrumb item.\n   * @default true\n   */\n  addSeparator?: boolean;\n}\n\nexport const Breadcrumb = React.forwardRef<HTMLElement, BreadcrumbProps>(\n  (\n    {\n      children,\n      className,\n      separator = <ChevronRight className=\"h-4 w-4\" />,\n      addSeparator = true,\n      ...props\n    },\n    forwardedRef,\n  ) => {\n    const validChildren = getValidChildren(children);\n    const clones = validChildren.map((child, index) => {\n      if (child.type === BreadcrumbItem) {\n        return React.cloneElement(child, {\n          addSeparator,\n          separator,\n          isLastChild: validChildren.length === index + 1,\n        });\n      }\n      if (child.type === React.Fragment) {\n        const fragmentChildren = getValidChildren(child.props.children);\n        const fragmentClones = fragmentChildren.map((fragmentChild, fragmentIndex) => {\n          return React.cloneElement(fragmentChild, {\n            addSeparator,\n            separator,\n            isLastChild:\n              fragmentChildren.length === fragmentIndex + 1 && validChildren.length === index + 1,\n          });\n        });\n        return fragmentClones;\n      }\n      return child;\n    });\n\n    return (\n      <nav\n        className={cn('relative break-words', className)}\n        aria-label=\"breadcrumb\"\n        {...props}\n        ref={forwardedRef}\n      >\n        <ol className=\"flex items-center\">{clones}</ol>\n      </nav>\n    );\n  },\n);\nBreadcrumb.displayName = 'Breadcrumb';\n\nexport interface BreadcrumbItemProps extends BreadcrumbProps {\n  isLastChild?: boolean;\n  to: string;\n  classNames?: {\n    navlink?: string;\n    separator?: string;\n  };\n}\n\nexport const BreadcrumbItem = React.forwardRef<HTMLLIElement, BreadcrumbItemProps>(\n  (\n    { children, className, classNames, isLastChild, separator, addSeparator, to, ...props },\n    forwardedRef,\n  ) => (\n    <li className={cn('inline-flex items-center', className)} {...props} ref={forwardedRef}>\n      <NavLink\n        to={to}\n        className={({ isActive }) =>\n          cn(\n            `text-sm font-medium ${\n              isActive\n                ? 'pointer-events-none aria-[current]:opacity-60'\n                : 'underline-offset-4 hover:underline'\n            } focus:outline-none focus:ring-2 focus:ring-focus`,\n            classNames?.navlink,\n          )\n        }\n        end\n      >\n        {children}\n      </NavLink>\n      {!isLastChild && addSeparator && (\n        <BreadcrumbSeparator className={classNames?.separator}>{separator}</BreadcrumbSeparator>\n      )}\n    </li>\n  ),\n);\nBreadcrumbItem.displayName = 'BreadcrumbItem';\n\nexport type BreadcrumbSeparatorProps = React.ComponentPropsWithoutRef<'span'>;\n\nexport const BreadcrumbSeparator = React.forwardRef<HTMLSpanElement, BreadcrumbSeparatorProps>(\n  ({ className, ...props }, forwardedRef) => {\n    return (\n      <span\n        className={cn('mx-2 opacity-50', className)}\n        role=\"presentation\"\n        {...props}\n        ref={forwardedRef}\n      />\n    );\n  },\n);\nBreadcrumbSeparator.displayName = 'BreadcrumbSeparator';\n"
  },
  {
    "path": "app/components/elements/Dialog.tsx",
    "content": "import * as React from 'react';\nimport * as DialogPrimitive from '@radix-ui/react-dialog';\nimport { cn } from '~/utils';\n\nimport Close from '~/assets/icons/CloseIcon';\n\nconst Dialog = DialogPrimitive.Root;\nconst DialogTrigger = DialogPrimitive.Trigger;\n\nconst DialogPortal = ({ ...props }: DialogPrimitive.DialogPortalProps) => (\n  <DialogPrimitive.DialogPortal {...props} />\n);\n\nconst DialogOverlay = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Overlay>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Overlay\n    ref={ref}\n    className={cn(\n      'fixed inset-0 z-[9998] cursor-pointer bg-black/[0.6] backdrop-blur-2xl data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',\n      className,\n    )}\n    {...props}\n  />\n));\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName;\n\nconst DialogContent = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {\n    hideCloseButton?: boolean;\n    container?: HTMLElement;\n    classNames?: {\n      overlay?: string;\n      content?: string;\n      closeButton?: string;\n    };\n  }\n>(({ className, classNames, children, hideCloseButton, container, ...props }, ref) => (\n  <DialogPortal container={container}>\n    <DialogOverlay className={cn(classNames?.overlay)} />\n    <DialogPrimitive.Content\n      ref={ref}\n      className={cn(\n        'fixed left-1/2 top-1/2 z-[9999] mt-[-5vh] max-h-[85vh] min-h-[150px] min-w-[250px] translate-x-[-50%] translate-y-[-50%] rounded-medium border border-default-100 bg-content1 !p-6 shadow-medium will-change-transform focus:outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]',\n        className ? className : classNames?.content,\n      )}\n      {...props}\n    >\n      {children}\n      {!hideCloseButton ? (\n        <DialogPrimitive.Close\n          className={cn(\n            'absolute right-4 top-4 flex h-5 w-5 items-center justify-center rounded-small opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-primary-200 focus:ring-offset-2 disabled:pointer-events-none',\n            classNames?.closeButton,\n          )}\n        >\n          <Close className=\"h-4 w-4\" />\n          <span className=\"sr-only\">Close</span>\n        </DialogPrimitive.Close>\n      ) : null}\n    </DialogPrimitive.Content>\n  </DialogPortal>\n));\nDialogContent.displayName = DialogPrimitive.Content.displayName;\n\nconst DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (\n  <div className={cn('flex flex-col gap-y-1.5 text-center sm:text-left', className)} {...props} />\n);\nDialogHeader.displayName = 'DialogHeader';\n\nconst DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-x-2', className)}\n    {...props}\n  />\n);\nDialogFooter.displayName = 'DialogFooter';\n\nconst DialogTitle = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Title>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Title\n    ref={ref}\n    className={cn('text-lg font-semibold leading-none tracking-tight', className)}\n    {...props}\n  />\n));\nDialogTitle.displayName = DialogPrimitive.Title.displayName;\n\nconst DialogDescription = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Description>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Description\n    ref={ref}\n    className={cn('text-sm text-foreground/60', className)}\n    {...props}\n  />\n));\nDialogDescription.displayName = DialogPrimitive.Description.displayName;\n\nexport {\n  Dialog,\n  DialogTrigger,\n  DialogContent,\n  DialogHeader,\n  DialogFooter,\n  DialogTitle,\n  DialogDescription,\n};\n"
  },
  {
    "path": "app/components/elements/Image.tsx",
    "content": "import { forwardRef, useState } from 'react';\nimport { Image as NextuiImage, type ImageProps as NextuiImageProps } from '@nextui-org/image';\nimport { Image as RemixImage, type ImageProps as RemixImageProps } from 'remix-image';\n\n// @ts-ignore\ntype UseImageProps = NextuiImageProps & RemixImageProps;\n\nexport interface ImageProps extends Omit<UseImageProps, 'ref' | 'isBlurred' | 'as'> {}\n\nconst Image = forwardRef<React.ElementRef<typeof NextuiImage>, ImageProps>(\n  ({ loaderUrl, dprVariants, options, responsive, ...props }, ref) => {\n    const [isLoading, setIsLoading] = useState(true);\n    if (process.env.RESPONSIVE_IMAGES === 'ON') {\n      const loaderUrlImage =\n        loaderUrl ||\n        (process.env.NODE_ENV === 'development' ? '/api/image' : window.process.env.IMAGE_PROXY);\n      return (\n        <NextuiImage\n          ref={ref}\n          as={RemixImage}\n          isLoading={isLoading}\n          data-loaded={!isLoading}\n          onLoadingComplete={() => setIsLoading(false)}\n          loaderUrl={loaderUrlImage}\n          dprVariants={dprVariants}\n          options={options}\n          responsive={responsive}\n          {...props}\n        />\n      );\n    }\n    return <NextuiImage as=\"img\" ref={ref} {...props} />;\n  },\n);\nImage.displayName = NextuiImage.displayName;\n\nexport default Image;\n"
  },
  {
    "path": "app/components/elements/NavigationMenu.tsx",
    "content": "import { forwardRef, type ComponentPropsWithoutRef, type ElementRef } from 'react';\nimport * as NavigationMenuPrimitive from '@radix-ui/react-navigation-menu';\nimport { cn } from '~/utils';\nimport { tv } from 'tailwind-variants';\n\nimport ChevronRight from '~/assets/icons/ChevronRightIcon';\n\nconst NavigationMenuViewport = forwardRef<\n  ElementRef<typeof NavigationMenuPrimitive.Viewport>,\n  ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>\n>(({ className = '', ...props }, ref) => (\n  <NavigationMenuPrimitive.Viewport\n    className={cn(\n      'relative h-[var(--radix-navigation-menu-viewport-height)] w-full origin-[top_center] overflow-hidden rounded-[12px] border border-divider bg-default/60 shadow-lg backdrop-blur-2xl backdrop-contrast-125 backdrop-saturate-200 transition-[width,_height] duration-300 data-[orientation=horizontal]:mt-1.5 data-[orientation=vertical]:ml-[-8px] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 sm:w-[var(--radix-navigation-menu-viewport-width)]',\n      className,\n    )}\n    ref={ref}\n    {...props}\n  />\n));\nNavigationMenuViewport.displayName = NavigationMenuPrimitive.Viewport.displayName;\n\nconst NavigationMenu = forwardRef<\n  ElementRef<typeof NavigationMenuPrimitive.Root>,\n  ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root> & {\n    viewportPositionClassName?: string;\n  }\n>(({ className = '', children, viewportPositionClassName, orientation, ...props }, ref) => (\n  <NavigationMenuPrimitive.Root\n    ref={ref}\n    orientation={orientation}\n    className={cn('relative z-10 flex flex-1 items-center justify-center', className)}\n    {...props}\n  >\n    {children}\n    <div\n      data-orientation={orientation}\n      className={cn(\n        `absolute flex justify-center data-[orientation=horizontal]:left-0\n      data-[orientation=horizontal]:top-full data-[orientation=vertical]:left-full`,\n        viewportPositionClassName,\n      )}\n    >\n      <NavigationMenuViewport />\n    </div>\n  </NavigationMenuPrimitive.Root>\n));\nNavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName;\n\nconst navigationMenuListStyles = tv({\n  base: 'group flex flex-1 list-none items-center justify-center data-[orientation=vertical]:flex-col',\n});\nconst NavigationMenuList = forwardRef<\n  ElementRef<typeof NavigationMenuPrimitive.List>,\n  ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>\n>(({ className = '', ...props }, ref) => (\n  <NavigationMenuPrimitive.List\n    ref={ref}\n    className={navigationMenuListStyles({ class: className })}\n    {...props}\n  />\n));\nNavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName;\n\nconst NavigationMenuItem = NavigationMenuPrimitive.Item;\n\nconst navigationMenuTriggerStyle = tv({\n  base: `text-md group inline-flex h-10 w-max items-center justify-center\n  rounded-small bg-transparent px-4 py-2 font-medium text-foreground transition-colors hover:bg-default hover:text-default-foreground hover:opacity-80 focus:bg-default focus:text-default-foreground focus:outline-none disabled:pointer-events-none\n  disabled:opacity-50 data-[active]:bg-default data-[state=open]:bg-default data-[active]:text-default-foreground data-[state=open]:text-default-foreground`,\n  variants: {\n    active: {\n      true: 'bg-default text-default-foreground',\n      false: '',\n    },\n  },\n});\n\nconst NavigationMenuTrigger = forwardRef<\n  ElementRef<typeof NavigationMenuPrimitive.Trigger>,\n  ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger> & {\n    showArrow?: boolean;\n  }\n>(({ className = '', children, showArrow, ...props }, ref) => (\n  <NavigationMenuPrimitive.Trigger\n    ref={ref}\n    className={navigationMenuTriggerStyle({ class: `group ${className}` })}\n    {...props}\n  >\n    {children}{' '}\n    {showArrow ? (\n      <ChevronRight\n        className=\"relative top-[1px] ml-auto h-5 w-5 transition duration-400 group-data-[state=open]:rotate-180\"\n        aria-hidden=\"true\"\n      />\n    ) : null}\n  </NavigationMenuPrimitive.Trigger>\n));\nNavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName;\n\nconst navigationMenuContentStyle = tv({\n  base: [\n    'absolute left-0 top-0 w-full sm:w-auto',\n    'data-[motion^=from-]:animate-in data-[motion^=from-]:fade-in',\n    'data-[motion^=to-]:animate-out data-[motion^=to-]:fade-out',\n    'data-[motion=from-end]:slide-in-from-bottom-52',\n    'data-[motion=from-start]:slide-in-from-top-52',\n    'data-[motion=to-end]:slide-out-to-bottom-52',\n    'data-[motion=to-start]:slide-out-to-top-52',\n  ],\n});\nconst NavigationMenuContent = forwardRef<\n  ElementRef<typeof NavigationMenuPrimitive.Content>,\n  ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>\n>(({ className = '', ...props }, ref) => (\n  <NavigationMenuPrimitive.Content\n    ref={ref}\n    className={navigationMenuContentStyle({ class: className })}\n    {...props}\n  />\n));\nNavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName;\n\nconst NavigationMenuLink = NavigationMenuPrimitive.Link;\n\nconst navigationMenuIndicatorStyle = tv({\n  base: `top-full z-[1] flex\n  h-2.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in`,\n});\nconst NavigationMenuIndicator = forwardRef<\n  ElementRef<typeof NavigationMenuPrimitive.Indicator>,\n  ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>\n>(({ className = '', ...props }, ref) => (\n  <NavigationMenuPrimitive.Indicator\n    ref={ref}\n    className={navigationMenuIndicatorStyle({ class: className })}\n    {...props}\n  >\n    <div className=\"relative top-[60%] h-2 w-2 rotate-45 rounded-tl-small bg-slate-200 shadow-md dark:bg-slate-800\" />\n  </NavigationMenuPrimitive.Indicator>\n));\nNavigationMenuIndicator.displayName = NavigationMenuPrimitive.Indicator.displayName;\n\nexport {\n  navigationMenuTriggerStyle,\n  NavigationMenu,\n  NavigationMenuList,\n  NavigationMenuItem,\n  NavigationMenuContent,\n  NavigationMenuTrigger,\n  NavigationMenuLink,\n  NavigationMenuIndicator,\n  NavigationMenuViewport,\n};\n"
  },
  {
    "path": "app/components/elements/Popover.tsx",
    "content": "import { forwardRef } from 'react';\nimport * as PopoverPrimitive from '@radix-ui/react-popover';\nimport { cn } from '~/utils';\n\nconst Popover = PopoverPrimitive.Root;\n\nconst PopoverTrigger = PopoverPrimitive.Trigger;\n\nconst PopoverContent = forwardRef<\n  React.ElementRef<typeof PopoverPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> & {\n    container?: HTMLElement | undefined;\n  }\n>(({ className, align = 'center', sideOffset = 4, container, ...props }, ref) => (\n  <PopoverPrimitive.Portal container={container}>\n    <PopoverPrimitive.Content\n      ref={ref}\n      align={align}\n      sideOffset={sideOffset}\n      className={cn(\n        'z-[9999] min-h-[50px] min-w-[100px] overflow-hidden rounded-large border border-divider bg-content1 text-default-foreground shadow-lg shadow-default/10 outline-none animate-in 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',\n        className,\n      )}\n      {...props}\n    />\n  </PopoverPrimitive.Portal>\n));\nPopoverContent.displayName = PopoverPrimitive.Content.displayName;\n\nexport { Popover, PopoverTrigger, PopoverContent };\n"
  },
  {
    "path": "app/components/elements/ScrollArea.tsx",
    "content": "import * as React from 'react';\nimport * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';\nimport { cn } from '~/utils';\nimport { tv } from 'tailwind-variants';\n\nconst ScrollArea = React.forwardRef<\n  React.ElementRef<typeof ScrollAreaPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>\n>(({ className, children, ...props }, ref) => (\n  <ScrollAreaPrimitive.Root\n    ref={ref}\n    className={cn('relative overflow-hidden', className)}\n    {...props}\n  >\n    {children}\n  </ScrollAreaPrimitive.Root>\n));\nScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;\n\nconst ScrollViewport = React.forwardRef<\n  React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaViewport>,\n  React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaViewport>\n>(({ className, children, ...props }, ref) => (\n  <ScrollAreaPrimitive.ScrollAreaViewport\n    className={cn('h-full w-full rounded-[inherit]', className)}\n    ref={ref}\n    {...props}\n  >\n    {children}\n  </ScrollAreaPrimitive.ScrollAreaViewport>\n));\nScrollViewport.displayName = ScrollAreaPrimitive.ScrollAreaViewport.displayName;\n\nconst scrollbarStyles = tv({\n  base: 'z-[9999] flex touch-none select-none p-[1px]',\n  variants: {\n    orientation: {\n      vertical: 'h-full w-[7px]',\n      horizontal: 'h-[7px] flex-col',\n    },\n  },\n});\nconst ScrollBar = React.forwardRef<\n  React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,\n  React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>\n>(({ className, orientation = 'vertical', ...props }, ref) => (\n  <ScrollAreaPrimitive.ScrollAreaScrollbar\n    ref={ref}\n    orientation={orientation}\n    className={scrollbarStyles({ orientation, className })}\n    {...props}\n  >\n    <ScrollAreaPrimitive.ScrollAreaThumb className=\"relative flex-1 rounded-full bg-default-800 duration-150 ease-out transition-background hover:bg-default-600\" />\n  </ScrollAreaPrimitive.ScrollAreaScrollbar>\n));\nScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;\n\nconst ScrollCorner = ScrollAreaPrimitive.ScrollAreaCorner;\n\nexport { ScrollArea, ScrollBar, ScrollViewport, ScrollCorner };\n"
  },
  {
    "path": "app/components/elements/SearchForm.tsx",
    "content": "import { useState } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { Input } from '@nextui-org/input';\nimport { Form } from '@remix-run/react';\n\ninterface ISearchForm {\n  onSubmit: (value: string) => void;\n  textPlaceHolder?: string;\n  textHelper?: string;\n  textOnButton: string;\n  defaultValue?: string;\n}\n\nconst SearchForm = (props: ISearchForm) => {\n  const { onSubmit, textOnButton, textHelper, textPlaceHolder, defaultValue } = props;\n  const [value, setValue] = useState<string | undefined>(defaultValue ?? '');\n\n  const submitHandler = (event: React.FormEvent<HTMLFormElement>) => {\n    event.preventDefault();\n    onSubmit(value ?? '');\n  };\n\n  return (\n    <Form\n      onSubmit={submitHandler}\n      className=\"mb-4 mt-10 flex w-full flex-row items-start justify-center gap-4\"\n    >\n      <Input\n        value={value}\n        onValueChange={setValue}\n        onClear={() => setValue('')}\n        label={textPlaceHolder}\n        variant=\"faded\"\n        color=\"default\"\n        fullWidth\n        description={textHelper}\n      />\n      <Button color=\"primary\" type=\"submit\" size=\"lg\" className=\"h-[3.4rem]\">\n        {textOnButton}\n      </Button>\n    </Form>\n  );\n};\n\nexport default SearchForm;\n"
  },
  {
    "path": "app/components/elements/Select.tsx",
    "content": "import * as React from 'react';\nimport * as SelectPrimitive from '@radix-ui/react-select';\nimport { cn } from '~/utils';\n\nimport ChevronDown from '~/assets/icons/ChevronDownIcon';\nimport ChevronUp from '~/assets/icons/ChevronUpIcon';\nimport Tick from '~/assets/icons/TickIcon';\n\nconst Select = SelectPrimitive.Root;\n\nconst SelectGroup = SelectPrimitive.Group;\n\nconst SelectValue = SelectPrimitive.Value;\n\nconst SelectTrigger = React.forwardRef<\n  React.ElementRef<typeof SelectPrimitive.Trigger>,\n  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> & {\n    selectIcon?: React.ReactNode;\n  }\n>(\n  (\n    {\n      className,\n      children,\n      selectIcon = (\n        <ChevronDown className=\"h-4 w-4 opacity-50 transition duration-200 group-data-[state=open]:rotate-180\" />\n      ),\n      ...props\n    },\n    ref,\n  ) => (\n    <SelectPrimitive.Trigger\n      ref={ref}\n      className={cn(\n        'group flex h-10 w-full items-center justify-between gap-3 rounded-medium border-2 border-default bg-transparent !px-3 !py-2 text-sm hover:bg-default focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=open]:bg-default data-[placeholder]:text-default-foreground',\n        className,\n      )}\n      {...props}\n    >\n      {children}\n      <SelectPrimitive.Icon className=\"flex items-center justify-center\">\n        {selectIcon}\n      </SelectPrimitive.Icon>\n    </SelectPrimitive.Trigger>\n  ),\n);\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName;\n\nconst SelectContent = React.forwardRef<\n  React.ElementRef<typeof SelectPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> & {\n    container?: HTMLElement;\n  }\n>(\n  (\n    { className, children, container, position = 'item-aligned', align = 'center', ...props },\n    ref,\n  ) => (\n    <SelectPrimitive.Portal container={container}>\n      <SelectPrimitive.Content\n        ref={ref}\n        className={cn(\n          'relative z-[9999] min-w-[8rem] overflow-hidden rounded-medium border border-divider bg-content1 text-default-foreground shadow-xl shadow-default/10  data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=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',\n          position === 'popper'\n            ? 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1'\n            : '!p-1.5',\n          className,\n        )}\n        position={position}\n        align={align}\n        {...props}\n      >\n        {position === 'item-aligned' ? (\n          <SelectPrimitive.ScrollUpButton className=\"flex h-6 cursor-default items-center justify-center bg-content1 text-default-foreground\">\n            <ChevronUp className=\"h-4 w-4\" />\n          </SelectPrimitive.ScrollUpButton>\n        ) : null}\n        <SelectPrimitive.Viewport\n          className={cn(\n            '!p-1',\n            position === 'popper'\n              ? 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'\n              : '',\n          )}\n        >\n          {children}\n        </SelectPrimitive.Viewport>\n        {position === 'item-aligned' ? (\n          <SelectPrimitive.ScrollDownButton className=\"flex h-6 cursor-default items-center justify-center bg-content1 text-default-foreground\">\n            <ChevronDown className=\"h-4 w-4\" />\n          </SelectPrimitive.ScrollDownButton>\n        ) : null}\n      </SelectPrimitive.Content>\n    </SelectPrimitive.Portal>\n  ),\n);\nSelectContent.displayName = SelectPrimitive.Content.displayName;\n\nconst SelectLabel = React.forwardRef<\n  React.ElementRef<typeof SelectPrimitive.Label>,\n  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>\n>(({ className, ...props }, ref) => (\n  <SelectPrimitive.Label\n    ref={ref}\n    className={cn('!py-1.5 !pl-8 !pr-2 text-sm font-semibold', className)}\n    {...props}\n  />\n));\nSelectLabel.displayName = SelectPrimitive.Label.displayName;\n\nconst SelectItem = React.forwardRef<\n  React.ElementRef<typeof SelectPrimitive.Item>,\n  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>\n>(({ className, children, ...props }, ref) => (\n  <SelectPrimitive.Item\n    ref={ref}\n    className={cn(\n      'relative flex w-full cursor-default select-none items-center rounded-small !py-1.5 !pl-8 !pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-default data-[highlighted]:text-default-foreground data-[disabled]:opacity-50',\n      className,\n    )}\n    {...props}\n  >\n    <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n      <SelectPrimitive.ItemIndicator>\n        <Tick className=\"h-4 w-4\" />\n      </SelectPrimitive.ItemIndicator>\n    </span>\n\n    <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n  </SelectPrimitive.Item>\n));\nSelectItem.displayName = SelectPrimitive.Item.displayName;\n\nconst SelectSeparator = React.forwardRef<\n  React.ElementRef<typeof SelectPrimitive.Separator>,\n  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n  <SelectPrimitive.Separator\n    ref={ref}\n    className={cn('-mx-1 my-1 h-px bg-primary-700', className)}\n    {...props}\n  />\n));\nSelectSeparator.displayName = SelectPrimitive.Separator.displayName;\n\nexport {\n  Select,\n  SelectGroup,\n  SelectValue,\n  SelectTrigger,\n  SelectContent,\n  SelectLabel,\n  SelectItem,\n  SelectSeparator,\n};\n"
  },
  {
    "path": "app/components/elements/Sheet.tsx",
    "content": "import * as React from 'react';\nimport * as SheetPrimitive from '@radix-ui/react-dialog';\nimport { cn } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { tv, type VariantProps } from 'tailwind-variants';\n\nimport Close from '~/assets/icons/CloseIcon';\n\nconst Sheet = SheetPrimitive.Root;\n\nconst SheetTrigger = SheetPrimitive.Trigger;\n\nconst SheetPortal = ({ ...props }: SheetPrimitive.DialogPortalProps) => (\n  <SheetPrimitive.Portal {...props} />\n);\nSheetPortal.displayName = SheetPrimitive.Portal.displayName;\n\nconst SheetOverlay = React.forwardRef<\n  React.ElementRef<typeof SheetPrimitive.Overlay>,\n  React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n  <SheetPrimitive.Overlay\n    ref={ref}\n    className={cn(\n      'fixed inset-0 z-[9998] cursor-pointer bg-background/[0.6] backdrop-blur-2xl data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',\n      className,\n    )}\n    {...props}\n  />\n));\nSheetOverlay.displayName = SheetPrimitive.Overlay.displayName;\n\nconst sheetContentStyles = tv({\n  base: 'fixed inset-y-0 z-[9999] w-[250px] bg-content1 !p-1 shadow-large transition ease-in-out will-change-transform focus:outline-none data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out sm:!p-6',\n  variants: {\n    side: {\n      top: 'bottom-auto w-full rounded-b-large data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',\n      right:\n        'right-0 h-full rounded-l-large data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right',\n      bottom:\n        'bottom-0 top-auto w-full rounded-t-large data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',\n      left: 'left-0 h-full rounded-r-large data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left',\n    },\n    size: {\n      content: '',\n      default: '',\n      sm: '',\n      lg: '',\n      xl: '',\n      full: '',\n    },\n  },\n  compoundVariants: [\n    {\n      side: ['top', 'bottom'],\n      size: 'content',\n      class: 'max-h-screen min-h-[150px]',\n    },\n    {\n      side: ['top', 'bottom'],\n      size: 'default',\n      class: 'h-1/3 min-h-[150px]',\n    },\n    {\n      side: ['top', 'bottom'],\n      size: 'sm',\n      class: 'h-1/4 min-h-[150px]',\n    },\n    {\n      side: ['top', 'bottom'],\n      size: 'lg',\n      class: 'h-1/2 min-h-[150px]',\n    },\n    {\n      side: ['top', 'bottom'],\n      size: 'xl',\n      class: 'h-5/6 min-h-[150px]',\n    },\n    {\n      side: ['top', 'bottom'],\n      size: 'full',\n      class: 'h-screen',\n    },\n    {\n      side: ['right', 'left'],\n      size: 'content',\n      class: 'max-w-screen min-w-[250px]',\n    },\n    {\n      side: ['right', 'left'],\n      size: 'default',\n      class: 'w-1/3 min-w-[250px]',\n    },\n    {\n      side: ['right', 'left'],\n      size: 'sm',\n      class: 'w-1/4 min-w-[250px]',\n    },\n    {\n      side: ['right', 'left'],\n      size: 'lg',\n      class: 'w-1/2 min-w-[250px]',\n    },\n    {\n      side: ['right', 'left'],\n      size: 'xl',\n      class: 'w-5/6 min-w-[250px]',\n    },\n    {\n      side: ['right', 'left'],\n      size: 'full',\n      class: 'w-screen',\n    },\n  ],\n  defaultVariants: {\n    side: 'right',\n    size: 'default',\n  },\n});\n\nexport interface SheetContentProps\n  extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,\n    VariantProps<typeof sheetContentStyles> {\n  hideCloseButton?: boolean;\n  swipeDownToClose?: boolean;\n  open?: boolean;\n  onOpenChange?: () => void;\n  container?: HTMLElement;\n  classNames?: {\n    overlay?: string;\n    content?: string;\n    closeButton?: string;\n  };\n}\n\nconst SheetContent = React.forwardRef<\n  React.ElementRef<typeof SheetPrimitive.Content>,\n  SheetContentProps\n>(\n  (\n    {\n      children,\n      hideCloseButton,\n      swipeDownToClose,\n      open,\n      onOpenChange,\n      container,\n      className,\n      classNames,\n      side,\n      size,\n      ...props\n    },\n    forwardedRef,\n  ) => {\n    const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n      if (info.offset.y > 100 && open && onOpenChange && swipeDownToClose && side === 'bottom') {\n        onOpenChange();\n      }\n    };\n    return (\n      <SheetPortal container={container}>\n        <SheetOverlay className={classNames?.overlay} />\n        <SheetPrimitive.Content\n          className={cn(\n            sheetContentStyles({ side, size }),\n            className ? className : classNames?.content,\n          )}\n          {...props}\n          ref={forwardedRef}\n          asChild\n        >\n          <motion.div\n            drag={swipeDownToClose && side === 'bottom' ? 'y' : false}\n            dragDirectionLock\n            dragConstraints={{ top: 0, bottom: 300 }}\n            dragElastic={{ top: 0, bottom: 0.5 }}\n            dragMomentum={false}\n            onDragEnd={handleDragEnd}\n            dragTransition={{ bounceStiffness: 1000, bounceDamping: 50 }}\n          >\n            {swipeDownToClose && side === 'bottom' ? (\n              <div className=\"!m-[1rem_auto_0] h-1 w-[75px] rounded-small bg-default-foreground\" />\n            ) : null}\n            {!hideCloseButton ? (\n              <SheetPrimitive.Close className=\"absolute right-4 top-4 flex h-5 w-5 items-center justify-center rounded-small opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-primary-200 focus:ring-offset-2 disabled:pointer-events-none\">\n                <Close className=\"h-4 w-4\" />\n                <span className=\"sr-only\">Close</span>\n              </SheetPrimitive.Close>\n            ) : null}\n            <motion.div\n              drag=\"y\"\n              dragConstraints={{ top: 0, bottom: 0 }}\n              dragElastic={0}\n              dragMomentum={false}\n              className={swipeDownToClose && side === 'bottom' ? '!mt-2' : ''}\n            >\n              {children}\n            </motion.div>\n          </motion.div>\n        </SheetPrimitive.Content>\n      </SheetPortal>\n    );\n  },\n);\n\nSheetContent.displayName = 'SheetContent';\n\nconst SheetHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (\n  <div className={cn('flex flex-col gap-y-2 text-center sm:text-left', className)} {...props} />\n);\nSheetHeader.displayName = 'SheetHeader';\n\nconst SheetFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn('flex flex-col-reverse gap-y-2 sm:flex-row sm:justify-end sm:gap-x-2', className)}\n    {...props}\n  />\n);\nSheetFooter.displayName = 'SheetFooter';\n\nconst SheetTitle = React.forwardRef<\n  React.ElementRef<typeof SheetPrimitive.Title>,\n  React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>\n>(({ className, ...props }, ref) => (\n  <SheetPrimitive.Title\n    ref={ref}\n    className={cn('font-semibold text-default-foreground', className)}\n    {...props}\n  />\n));\nSheetTitle.displayName = SheetPrimitive.Title.displayName;\n\nconst SheetDescription = React.forwardRef<\n  React.ElementRef<typeof SheetPrimitive.Description>,\n  React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>\n>(({ className, ...props }, ref) => (\n  <SheetPrimitive.Description\n    ref={ref}\n    className={cn('text-muted-foreground text-sm', className)}\n    {...props}\n  />\n));\nSheetDescription.displayName = SheetPrimitive.Description.displayName;\n\nexport {\n  Sheet,\n  SheetTrigger,\n  SheetContent,\n  SheetHeader,\n  SheetFooter,\n  SheetTitle,\n  SheetDescription,\n};\n"
  },
  {
    "path": "app/components/elements/Slider.tsx",
    "content": "import * as React from 'react';\nimport { Badge } from '@nextui-org/badge';\nimport * as SliderPrimitive from '@radix-ui/react-slider';\nimport { useHover } from '@react-aria/interactions';\nimport { cn } from '~/utils';\nimport { tv } from 'tailwind-variants';\n\nconst sliderStyles = tv({\n  slots: {\n    track:\n      'relative w-full grow overflow-hidden rounded-full data-[orientation=horizontal]:h-2 data-[orientation=vertical]:w-2',\n    range: 'absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full',\n    thumb:\n      'focus-visible:ring-ring block h-5 w-5 rounded-full border-2 bg-background ring-offset-background transition-colors hover:cursor-grab focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',\n  },\n  variants: {\n    color: {\n      default: {\n        track: 'bg-default-50',\n        range: 'bg-default',\n        thumb: 'border-foreground',\n      },\n      primary: {\n        track: 'bg-primary-50',\n        range: 'bg-primary',\n        thumb: 'border-foreground',\n      },\n      secondary: {\n        track: 'bg-secondary-50',\n        range: 'bg-secondary',\n        thumb: 'border-foreground',\n      },\n      success: {\n        track: 'bg-success-50',\n        range: 'bg-success',\n        thumb: 'border-foreground',\n      },\n      warning: {\n        track: 'bg-warning-50',\n        range: 'bg-warning',\n        thumb: 'border-foreground',\n      },\n      danger: {\n        track: 'bg-danger-50',\n        range: 'bg-danger',\n        thumb: 'border-foreground',\n      },\n      gradient: {\n        track: 'bg-default',\n        range: 'bg-gradient-to-r from-primary to-secondary',\n        thumb: 'border-foreground',\n      },\n    },\n  },\n  defaultVariants: {\n    color: 'default',\n  },\n});\n\nconst Slider = React.forwardRef<\n  React.ElementRef<typeof SliderPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root> & {\n    showValueOnHover?: boolean;\n    color?: 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'gradient';\n  }\n>(\n  (\n    { className, defaultValue, value, showValueOnHover = false, color = 'default', ...props },\n    ref,\n  ) => {\n    const hasRange = Array.isArray(defaultValue || value);\n    const thumbsArray = hasRange ? defaultValue || value : [defaultValue || value];\n    const [isFirstThumbInvisible, setIsFirstThumbInvisible] = React.useState(true);\n    const [isSecondThumbInvisible, setIsSecondThumbInvisible] = React.useState(true);\n    const { hoverProps: firstThumbProps } = useHover({\n      onHoverStart: () => setIsFirstThumbInvisible(false),\n      onHoverEnd: () => setIsFirstThumbInvisible(true),\n    });\n    const { hoverProps: secondThumbProps } = useHover({\n      onHoverStart: () => setIsSecondThumbInvisible(false),\n      onHoverEnd: () => setIsSecondThumbInvisible(true),\n    });\n    const { track, range, thumb } = sliderStyles({ color });\n    return (\n      <SliderPrimitive.Root\n        ref={ref}\n        className={cn(\n          'relative flex w-full touch-none select-none items-center data-[orientation=horizontal]:h-10 data-[orientation=vertical]:h-24 data-[orientation=vertical]:w-10 data-[orientation=vertical]:flex-col',\n          className,\n        )}\n        defaultValue={defaultValue}\n        value={value}\n        {...props}\n      >\n        <SliderPrimitive.Track className={track()}>\n          <SliderPrimitive.Range className={range()} />\n        </SliderPrimitive.Track>\n        {thumbsArray?.map((value, index) => (\n          <SliderPrimitive.Thumb key={index} className={thumb()}>\n            {showValueOnHover ? (\n              <Badge\n                content={value}\n                variant=\"flat\"\n                size=\"sm\"\n                color={color === 'gradient' ? 'default' : color}\n                isInvisible={index === 0 ? isFirstThumbInvisible : isSecondThumbInvisible}\n                disableOutline\n                placement=\"top-right\"\n                classNames={{\n                  base: '',\n                  badge: 'right-[60%] top-[-100%]',\n                }}\n              >\n                <div className=\"h-5 w-5\" {...(index === 0 ? firstThumbProps : secondThumbProps)} />\n              </Badge>\n            ) : null}\n          </SliderPrimitive.Thumb>\n        ))}\n      </SliderPrimitive.Root>\n    );\n  },\n);\nSlider.displayName = SliderPrimitive.Root.displayName;\n\nexport default Slider;\n"
  },
  {
    "path": "app/components/elements/dialog/AddSubtitleDialog.tsx",
    "content": "import { useState } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { toast } from 'sonner';\n\nimport { getExt } from '~/utils/file';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport usePlayerState from '~/store/player/usePlayerState';\nimport { DialogFooter, DialogHeader, DialogTitle } from '~/components/elements/Dialog';\n\ninterface IAddSubtitlesProps {\n  artplayer: Artplayer | null;\n  setCurrentSubtitle: React.Dispatch<React.SetStateAction<string>>;\n}\n\nconst AddSubtitles = (props: IAddSubtitlesProps) => {\n  const { artplayer, setCurrentSubtitle } = props;\n  const [disabledSubmit, setDisabledSubmit] = useState(true);\n  const [subtitle, setSubtitle] = useState<File | null>(null);\n  const { updateSubtitleSelector } = usePlayerState((state) => state);\n  const { autoSwitchSubtitle } = useSoraSettings();\n\n  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n    const file = e.target.files?.[0];\n    if (!file) return;\n    const type = getExt(file.name);\n    if (!type || !['ass', 'vtt', 'srt'].includes(type)) {\n      toast.error('Invalid file type', {\n        description: 'Type support: .srt, .vtt, .ass',\n        duration: 5000,\n      });\n      return;\n    }\n    setDisabledSubmit(false);\n    setSubtitle(file);\n  };\n\n  const handleSubtitleSubmit = () => {\n    if (subtitle) {\n      setDisabledSubmit(true);\n      const type = getExt(subtitle.name);\n      const url = URL.createObjectURL(subtitle);\n      const subtitleName =\n        subtitle.name.length > 20\n          ? `${subtitle.name.substring(0, 10)}...${subtitle.name.substring(\n              subtitle.name.length - 10,\n              subtitle.name.length,\n            )}`\n          : subtitle.name;\n      const newSubtitle = [\n        {\n          html: subtitleName,\n          url,\n          type,\n        },\n      ];\n      updateSubtitleSelector(newSubtitle);\n      if (artplayer && autoSwitchSubtitle.value) {\n        artplayer.subtitle.switch(url, {\n          name: subtitleName,\n          type,\n        });\n        setCurrentSubtitle(subtitleName);\n        toast.success('Subtitle added successfully', {\n          description: 'The subtitle has been switched automatically',\n          duration: 3000,\n        });\n      } else {\n        toast.success('Subtitle added successfully', {\n          description: 'You can choose the subtitle in the subtitles list',\n          duration: 3000,\n        });\n      }\n    }\n  };\n\n  return (\n    <>\n      <DialogHeader>\n        <DialogTitle className=\"!mb-3\">Add Subtitle</DialogTitle>\n      </DialogHeader>\n      <div className=\"mb-3 w-full\">\n        <input\n          type=\"file\"\n          id=\"subtitle\"\n          name=\"subtitle\"\n          accept=\".srt,.vtt,.ass\"\n          onChange={(e) => handleFileChange(e)}\n          className=\"flex h-10 w-full rounded-small border border-divider bg-transparent !px-3 !py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-foreground/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus focus-visible:ring-offset-2\"\n        />\n      </div>\n      <DialogFooter>\n        <Button\n          type=\"submit\"\n          size=\"md\"\n          color=\"primary\"\n          isDisabled={disabledSubmit}\n          onPress={() => handleSubtitleSubmit()}\n          className=\"!px-4\"\n        >\n          Add\n        </Button>\n      </DialogFooter>\n    </>\n  );\n};\n\nexport default AddSubtitles;\n"
  },
  {
    "path": "app/components/elements/dialog/SearchSubtitleDialog.tsx",
    "content": "import { useEffect, useState } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { Input } from '@nextui-org/input';\nimport { Pagination } from '@nextui-org/pagination';\nimport { useMediaQuery } from '@react-hookz/web';\nimport { useFetcher } from '@remix-run/react';\nimport { toast } from 'sonner';\n\nimport type {\n  ISubtitle,\n  ISubtitleDownload,\n  ISubtitlesSearch,\n} from '~/services/open-subtitles/open-subtitles.types';\nimport { useGlobalLoadingState } from '~/utils/react/hooks/useGlobalNavigationState';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport usePlayerState from '~/store/player/usePlayerState';\nimport { DialogHeader, DialogTitle } from '~/components/elements/Dialog';\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '~/components/elements/Select';\n\ninterface ISearchSubtitlesProps {\n  artplayer: Artplayer | null;\n  containerPortal?: HTMLElement;\n  subtitleOptions?: {\n    imdb_id?: number;\n    tmdb_id?: number;\n    parent_feature_id?: number;\n    parent_imdb_id?: number;\n    parent_tmdb_id?: number;\n    episode_number?: number;\n    season_number?: number;\n    type?: 'movie' | 'episode' | 'all';\n    title?: string;\n    sub_format: 'srt' | 'webvtt';\n  };\n  setCurrentSubtitle: React.Dispatch<React.SetStateAction<string>>;\n}\n\nconst SearchSubtitles = (props: ISearchSubtitlesProps) => {\n  const { artplayer, subtitleOptions, containerPortal, setCurrentSubtitle } = props;\n  const rootData = useTypedRouteLoaderData('root');\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const fetcher = useFetcher();\n  const globalState = useGlobalLoadingState();\n  const { updateSubtitleSelector } = usePlayerState((state) => state);\n\n  const preInput: string | undefined =\n    subtitleOptions?.type === 'movie'\n      ? subtitleOptions?.title\n      : subtitleOptions?.type === 'episode'\n      ? `${subtitleOptions?.title} ${\n          subtitleOptions?.season_number ? `S${subtitleOptions?.season_number}` : ''\n        } ${subtitleOptions?.episode_number ? `E${subtitleOptions?.episode_number}` : ''}`\n      : '';\n\n  const [value, setValue] = useState<string>(preInput || '');\n  const [language, setLanguage] = useState<string>();\n  const [page, setPage] = useState<number>(1);\n  const [totalPages, setTotalPages] = useState<number>(0);\n  const [subtitle, setSubtitle] = useState<ISubtitle>();\n  const [subtitlesSearch, setSubtitlesSearch] = useState<ISubtitlesSearch>();\n  const [isGetSubtitleLink, setIsGetSubtitleLink] = useState<boolean>(false);\n  const { autoSwitchSubtitle } = useSoraSettings();\n\n  const handlePageChange = (page: number) => {\n    setSubtitlesSearch(undefined);\n    let url = '/api/subtitles/search';\n    const params = new URLSearchParams();\n    if (value) {\n      params.append('query', value);\n    }\n    if (language) {\n      params.append('language', language);\n    }\n    if (subtitleOptions && subtitleOptions.imdb_id) {\n      params.append('imdb_id', `${subtitleOptions.imdb_id}`);\n    }\n    if (subtitleOptions && subtitleOptions.tmdb_id) {\n      params.append('tmdb_id', `${subtitleOptions.tmdb_id}`);\n    }\n    if (subtitleOptions && subtitleOptions.parent_feature_id) {\n      params.append('parent_feature_id', `${subtitleOptions.parent_feature_id}`);\n    }\n    if (subtitleOptions && subtitleOptions.parent_imdb_id) {\n      params.append('parent_imdb_id', `${subtitleOptions.parent_imdb_id}`);\n    }\n    if (subtitleOptions && subtitleOptions.parent_tmdb_id) {\n      params.append('parent_tmdb_id', `${subtitleOptions.parent_tmdb_id}`);\n    }\n    if (subtitleOptions && subtitleOptions.episode_number !== undefined) {\n      params.append('episode_number', `${subtitleOptions.episode_number}`);\n    }\n    if (subtitleOptions && subtitleOptions.season_number !== undefined) {\n      params.append('season_number', `${subtitleOptions.season_number}`);\n    }\n    if (subtitleOptions && subtitleOptions.type) {\n      params.append('type', subtitleOptions.type);\n    }\n    if (page) {\n      params.append('page', `${page}`);\n    }\n    url += `?${params}`;\n    fetcher.load(url);\n  };\n\n  const handleSubtitleClick = (subtitle: ISubtitle) => {\n    setSubtitle(subtitle);\n    setIsGetSubtitleLink(true);\n    fetcher.load(\n      `/api/subtitles/download?file_id=${subtitle.attributes.files[0].file_id}&sub_format=${subtitleOptions?.sub_format}`,\n    );\n  };\n\n  const searchSubtitles = () => {\n    setSubtitlesSearch(undefined);\n    let url = '/api/subtitles/search';\n    const params = new URLSearchParams();\n    if (value) {\n      params.append('query', value);\n    }\n    if (language) {\n      params.append('language', language);\n    }\n    if (subtitleOptions && subtitleOptions.imdb_id) {\n      params.append('imdb_id', `${subtitleOptions.imdb_id}`);\n    }\n    if (subtitleOptions && subtitleOptions.tmdb_id) {\n      params.append('tmdb_id', `${subtitleOptions.tmdb_id}`);\n    }\n    if (subtitleOptions && subtitleOptions.parent_feature_id) {\n      params.append('parent_feature_id', `${subtitleOptions.parent_feature_id}`);\n    }\n    if (subtitleOptions && subtitleOptions.parent_imdb_id) {\n      params.append('parent_imdb_id', `${subtitleOptions.parent_imdb_id}`);\n    }\n    if (subtitleOptions && subtitleOptions.parent_tmdb_id) {\n      params.append('parent_tmdb_id', `${subtitleOptions.parent_tmdb_id}`);\n    }\n    if (subtitleOptions && subtitleOptions.episode_number !== undefined) {\n      params.append('episode_number', `${subtitleOptions.episode_number}`);\n    }\n    if (subtitleOptions && subtitleOptions.season_number !== undefined) {\n      params.append('season_number', `${subtitleOptions.season_number}`);\n    }\n    if (subtitleOptions && subtitleOptions.type) {\n      params.append('type', subtitleOptions.type);\n    }\n    url += `?${params}`;\n    fetcher.load(url);\n  };\n\n  useEffect(() => {\n    if (fetcher.data && (fetcher.data as { subtitlesSearch: ISubtitlesSearch }).subtitlesSearch) {\n      setSubtitlesSearch((fetcher.data as { subtitlesSearch: ISubtitlesSearch }).subtitlesSearch);\n      setPage((fetcher.data as { subtitlesSearch: ISubtitlesSearch }).subtitlesSearch.page);\n      setTotalPages(\n        (fetcher.data as { subtitlesSearch: ISubtitlesSearch }).subtitlesSearch.total_pages,\n      );\n    }\n    if (fetcher.data && (fetcher.data as { subtitle: ISubtitleDownload }).subtitle) {\n      setIsGetSubtitleLink(false);\n      const subtitleName = subtitle?.attributes?.release || '';\n      const subtitleHtml =\n        subtitleName.length > 20\n          ? `${subtitleName.substring(0, 10)}...${subtitleName.substring(\n              subtitleName.length - 10,\n              subtitleName.length,\n            )}`\n          : subtitleName;\n      const url = (fetcher.data as { subtitle: ISubtitleDownload }).subtitle.link;\n      const newSubtitle = [\n        {\n          html: subtitleHtml,\n          url,\n        },\n      ];\n      updateSubtitleSelector(newSubtitle);\n      if (artplayer && autoSwitchSubtitle.value) {\n        artplayer.subtitle.switch(url, {\n          name: subtitleHtml,\n        });\n        setCurrentSubtitle(subtitleHtml);\n        toast.success('Subtitle added successfully', {\n          description: 'The subtitle has been switched automatically',\n          duration: 3000,\n        });\n      } else {\n        toast.success('Subtitle added successfully', {\n          description: 'You can choose the subtitle in the subtitles list',\n          duration: 3000,\n        });\n      }\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [fetcher.data]);\n\n  return (\n    <>\n      <DialogHeader className=\"!px-2 sm:!px-0\">\n        <DialogTitle className=\"!mb-3\">Search Subtitles</DialogTitle>\n        <div className=\"!mb-5 flex w-full flex-col items-end justify-start gap-6 sm:flex-row sm:items-center\">\n          <div className=\"flex w-full flex-col items-center justify-start gap-4 sm:flex-row sm:flex-wrap\">\n            <Input\n              value={value}\n              onValueChange={(value) => setValue(value || '')}\n              onClear={() => setValue('')}\n              size=\"sm\"\n              placeholder=\"Search Subtitle\"\n              variant=\"faded\"\n              color=\"default\"\n              type=\"text\"\n              className=\"w-full sm:w-auto\"\n            />\n            <Select value={language} onValueChange={(value: string) => setLanguage(value)}>\n              <SelectTrigger aria-label=\"Language\" className=\"h-10 sm:w-fit\">\n                <SelectValue placeholder=\"Select language\" />\n              </SelectTrigger>\n              <SelectContent container={containerPortal}>\n                {rootData?.languages &&\n                  rootData?.languages\n                    .sort((a, b) => {\n                      const textA = a.english_name.toUpperCase();\n                      const textB = b.english_name.toUpperCase();\n                      return textA < textB ? -1 : textA > textB ? 1 : 0;\n                    })\n                    .map((lang) => (\n                      <SelectItem value={lang.iso_639_1} key={`SelectItem${lang.iso_639_1}`}>\n                        {lang.english_name}\n                      </SelectItem>\n                    ))}\n              </SelectContent>\n            </Select>\n          </div>\n          <Button\n            type=\"button\"\n            color=\"primary\"\n            isDisabled={globalState === 'loading' && !isGetSubtitleLink}\n            isLoading={globalState === 'loading' && !isGetSubtitleLink}\n            className=\"!px-3\"\n            onPress={searchSubtitles}\n          >\n            Search\n          </Button>\n        </div>\n      </DialogHeader>\n      <div className=\"flex w-full flex-col gap-y-2\">\n        {globalState === 'loading' && !isGetSubtitleLink && (\n          <div role=\"status\" className=\"max-w-sm animate-pulse\">\n            <div className=\"!mb-4 h-2.5 w-48 rounded-full bg-gray-200 dark:bg-gray-700\" />\n            <div className=\"!mb-4 h-2 max-w-[360px] rounded-full bg-gray-200 dark:bg-gray-700\" />\n            <div className=\"!mb-4 h-2 rounded-full bg-gray-200 dark:bg-gray-700\" />\n            <div className=\"!mb-4 h-2 max-w-[330px] rounded-full bg-gray-200 dark:bg-gray-700\" />\n            <div className=\"!mb-4 h-2 max-w-[300px] rounded-full bg-gray-200 dark:bg-gray-700\" />\n            <div className=\"h-2 max-w-[360px] rounded-full bg-gray-200 dark:bg-gray-700\" />\n            <span className=\"sr-only\">Loading...</span>\n          </div>\n        )}\n        {subtitlesSearch &&\n          subtitlesSearch.data.map((subtitle) => (\n            <Button\n              key={subtitle.id}\n              type=\"button\"\n              variant=\"light\"\n              className=\"!px-3\"\n              onPress={() => handleSubtitleClick(subtitle)}\n            >\n              {subtitle.attributes.release} ({subtitle.attributes.language})\n            </Button>\n          ))}\n        {totalPages > 1 ? (\n          <div className=\"!mb-5 flex w-full flex-row items-center justify-center\">\n            <Pagination\n              // showControls={!isSm}\n              total={totalPages}\n              initialPage={page}\n              // shadow\n              onChange={handlePageChange}\n              {...(isSm && { size: 'sm' })}\n              className=\"[&>*]:!mx-[0.125rem] sm:[&>*]:!mx-1\"\n            />\n          </div>\n        ) : null}\n      </div>\n    </>\n  );\n};\n\nexport default SearchSubtitles;\n"
  },
  {
    "path": "app/components/elements/dialog/SelectProviderDialog.tsx",
    "content": "import { useEffect, useState } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { useFetcher, useNavigate } from '@remix-run/react';\n\nimport type { Provider } from '~/services/provider.server';\nimport { useGlobalLoadingState } from '~/utils/react/hooks/useGlobalNavigationState';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport { DialogHeader, DialogTitle } from '~/components/elements/Dialog';\n\ntype SelectProviderProps = {\n  id: number | string | undefined;\n  visible: boolean;\n  closeHandler: () => void;\n  type: 'movie' | 'tv' | 'anime';\n  title: string;\n  origTitle: string;\n  year: number;\n  season?: number;\n  episode?: number;\n  animeType?: string;\n  isEnded?: boolean;\n};\n\nconst SelectProvider = (props: SelectProviderProps) => {\n  const {\n    id,\n    visible,\n    closeHandler,\n    type,\n    title,\n    origTitle,\n    year,\n    season,\n    episode,\n    animeType,\n    isEnded,\n  } = props;\n  const fetcher = useFetcher();\n  const navigate = useNavigate();\n  const globalState = useGlobalLoadingState();\n  const { isShowSkipOpEdButton } = useSoraSettings();\n  const [provider, setProvider] = useState<Provider[]>();\n  const handleProvider = (item: Provider) => {\n    closeHandler();\n    if (type === 'movie') navigate(`/movies/${id}/watch?provider=${item.provider}&id=${item.id}`);\n    else if (type === 'tv')\n      navigate(\n        `/tv-shows/${id}/season/${season}/episode/${episode}/watch?provider=${item.provider}&id=${item.id}`,\n      );\n    else if (type === 'anime')\n      navigate(\n        `/anime/${id}/episode/${episode}/watch?provider=${item.provider}&id=${item.id}&episode=${episode}&skipOpEd=${isShowSkipOpEdButton.value}`,\n      );\n  };\n  useEffect(() => {\n    if (visible) {\n      setProvider([]);\n      if (type === 'movie')\n        fetcher.load(\n          `/api/provider?title=${title}&type=${type}&origTitle=${origTitle}&year=${year}&isEnded=${isEnded}&tmdbId=${id}`,\n        );\n      else if (type === 'tv')\n        fetcher.load(\n          `/api/provider?title=${title}&type=${type}&origTitle=${origTitle}&year=${year}&season=${season}&isEnded=${isEnded}&tmdbId=${id}`,\n        );\n      else if (type === 'anime')\n        fetcher.load(\n          `/api/provider?title=${title}&type=${type}&origTitle=${origTitle}&year=${year}&aid=${id}&animeType=${animeType}&isEnded=${isEnded}`,\n        );\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [visible]);\n\n  useEffect(() => {\n    if (fetcher.data && (fetcher.data as { provider: Provider[] | undefined }).provider) {\n      setProvider((fetcher.data as { provider: Provider[] | undefined }).provider);\n    }\n  }, [fetcher.data]);\n\n  return (\n    <>\n      <DialogHeader>\n        <DialogTitle>Select Provider</DialogTitle>\n      </DialogHeader>\n      <div className=\"mt-4 flex w-full flex-col items-center justify-center\">\n        {provider && Array.isArray(provider)\n          ? provider.map((item) => (\n              <Button\n                type=\"button\"\n                key={item.id}\n                variant=\"light\"\n                onPress={() => handleProvider(item)}\n              >\n                {item.provider}\n              </Button>\n            ))\n          : null}\n        {globalState === 'loading' ? (\n          <div role=\"status\" className=\"max-w-sm animate-pulse\">\n            <div className=\"mb-4 h-2.5 w-48 rounded-full bg-gray-200 dark:bg-gray-700\" />\n            <div className=\"mb-4 h-2.5 w-48 rounded-full bg-gray-200 dark:bg-gray-700\" />\n            <div className=\"mb-4 h-2.5 w-48 rounded-full bg-gray-200 dark:bg-gray-700\" />\n            <span className=\"sr-only\">Loading...</span>\n          </div>\n        ) : null}\n      </div>\n    </>\n  );\n};\n\nexport default SelectProvider;\n"
  },
  {
    "path": "app/components/elements/dialog/WatchTrailerDialog.tsx",
    "content": "import { useWindowSize } from '@react-hookz/web';\nimport YouTube, { type YouTubeProps } from 'react-youtube';\n\nimport { type ITrailer } from '~/services/consumet/anilist/anilist.types';\n\nexport type Trailer = {\n  iso_639_1?: string;\n  iso_3166_1?: string;\n  name?: string;\n  key?: string;\n  site?: string;\n  size?: number;\n  type?: string;\n  official?: boolean;\n  published_at?: string;\n  id?: string;\n};\n\ntype WatchTrailerProps = {\n  trailer: Trailer | ITrailer;\n  currentTime?: number;\n};\n\nconst WatchTrailer = ({ trailer, currentTime }: WatchTrailerProps) => {\n  const { width } = useWindowSize();\n  const opts: YouTubeProps['opts'] = {\n    height: `${width && width < 720 ? width / 1.5 : 480}`,\n    width: `${width && width < 720 ? width : 720}`,\n    playerVars: {\n      // https://developers.google.com/youtube/player_parameters\n      autoplay: 1,\n      modestbranding: 1,\n      controls: 1,\n      start: currentTime || 0,\n    },\n  };\n\n  if (trailer) {\n    return (\n      <YouTube\n        videoId={(trailer as Trailer).key || (trailer as ITrailer).id}\n        opts={opts}\n        onReady={({ target }) => {\n          target.playVideo();\n        }}\n      />\n    );\n  }\n  return <h4>No trailer found</h4>;\n};\n\nexport default WatchTrailer;\n"
  },
  {
    "path": "app/components/elements/player/ArtPlayer.tsx",
    "content": "import {\n  memo,\n  useEffect,\n  useRef,\n  useState,\n  type CSSProperties,\n  type Dispatch,\n  type SetStateAction,\n} from 'react';\nimport { Button } from '@nextui-org/button';\nimport { Link } from '@remix-run/react';\nimport Artplayer from 'artplayer';\nimport { isMobile } from 'react-device-detect';\n\nimport usePlayerState from '~/store/player/usePlayerState';\nimport AspectRatio from '~/components/elements/AspectRatio';\nimport Close from '~/assets/icons/CloseIcon';\n\ninterface IPlayerProps {\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  option: any;\n  getInstance: (art: Artplayer) => void;\n  style?: CSSProperties | undefined;\n  className?: string;\n  setIsPlayerPlaying: Dispatch<SetStateAction<boolean>>;\n}\n\nconst Player: React.FC<IPlayerProps> = (props: IPlayerProps) => {\n  const { option, getInstance, style, className, setIsPlayerPlaying, ...rest } = props;\n  const {\n    isMini,\n    setIsMini,\n    setShouldShowPlayer,\n    routePlayer,\n    setRoutePlayer,\n    titlePlayer,\n    setTitlePlayer,\n    setPlayerData,\n    setQualitySelector,\n    setSubtitleSelector,\n  } = usePlayerState((state) => state);\n  const [artplayer, setArtplayer] = useState<Artplayer | null>(null);\n  // const { isSwipeFullscreen } = useSoraSettings();\n  const artRef = useRef<HTMLDivElement>(null);\n\n  // // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  // const handleDragEnd = (event: MouseEvent | PointerEvent | TouchEvent, info: PanInfo) => {\n  //   if (artplayer && isSwipeFullscreen.value) {\n  //     if (!artplayer.fullscreen && info.offset.y < -100) {\n  //       artplayer.fullscreen = true;\n  //     }\n  //     if (artplayer.fullscreen && info.offset.y > 100) {\n  //       artplayer.fullscreen = false;\n  //     }\n  //   }\n  // };\n  useEffect(\n    () => {\n      const art = new Artplayer({\n        container: artRef.current,\n        ...option,\n      });\n      setArtplayer(art);\n      if (getInstance && typeof getInstance === 'function') {\n        getInstance(art);\n      }\n      return () => {\n        if (art && art.destroy) {\n          art.destroy(false);\n        }\n      };\n    },\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [],\n  );\n  return (\n    <AspectRatio\n      ratio={isMini ? undefined : isMobile ? 16 / 9 : 7 / 3}\n      className={isMini ? 'overflow-hidden rounded-medium' : ''}\n    >\n      <div\n        ref={artRef}\n        style={style}\n        className={className}\n        // drag={isMobile && isSwipeFullscreen ? 'y' : false}\n        // whileDrag={{ scale: 1.2 }}\n        // dragConstraints={{ top: 0, bottom: 0 }}\n        // dragSnapToOrigin\n        // dragElastic={0.8}\n        // onDragEnd={handleDragEnd}\n        {...rest}\n      />\n      {isMini ? (\n        <div className=\"inset-x-0 bottom-[-64px] flex h-16 flex-row items-center justify-between rounded-b-medium bg-default p-3\">\n          <Link\n            to={routePlayer}\n            className=\"line-clamp-1 !text-default-foreground focus:outline-none focus:ring-2 focus:ring-focus\"\n            title={titlePlayer}\n          >\n            {titlePlayer}\n          </Link>\n          <Button\n            type=\"button\"\n            size=\"md\"\n            variant=\"light\"\n            isIconOnly\n            onPress={() => {\n              if (artplayer) artplayer.destroy();\n              setShouldShowPlayer(false);\n              setPlayerData(undefined);\n              setIsMini(false);\n              setRoutePlayer('');\n              setTitlePlayer('');\n              setQualitySelector([]);\n              setSubtitleSelector([]);\n              setIsPlayerPlaying(false);\n            }}\n          >\n            <Close />\n          </Button>\n        </div>\n      ) : null}\n    </AspectRatio>\n  );\n};\n\nexport default memo(Player);\n"
  },
  {
    "path": "app/components/elements/player/PlayerError.tsx",
    "content": "import AspectRatio from '~/components/elements/AspectRatio';\n\ninterface IPlayerErrorProps {\n  title: string;\n  message: string;\n}\n\nconst PlayerError = (props: IPlayerErrorProps) => {\n  const { title, message } = props;\n  return (\n    <AspectRatio ratio={7 / 3}>\n      <div className=\"flex h-full w-full flex-col items-center justify-center\">\n        <h1 className=\"text-warning\">{title}</h1>\n        <p className=\"text-warning\">{message}</p>\n      </div>\n    </AspectRatio>\n  );\n};\n\nexport default PlayerError;\n"
  },
  {
    "path": "app/components/elements/player/PlayerHotkey.ts",
    "content": "import type { Dispatch, SetStateAction } from 'react';\nimport type Artplayer from 'artplayer';\n\nfunction PlayerHotKey(art: Artplayer, setShowSubtitle: Dispatch<SetStateAction<boolean>>) {\n  // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode\n  // art.hotkey.add(109, () => {\n  //   // NumpadSubtract\n  //   if (art.playbackRate > 0.25) {\n  //     art.playbackRate -= 0.25;\n  //     art.notice.show = `Speed ${art.playbackRate.toFixed(2)}x`;\n  //   } else {\n  //     art.notice.show = 'Min speed';\n  //   }\n  // });\n  // art.hotkey.add(107, () => {\n  //   // NumpadAdd\n  //   if (art.playbackRate < 2) {\n  //     art.playbackRate += 0.25;\n  //     art.notice.show = `Speed ${art.playbackRate.toFixed(2)}x`;\n  //   } else {\n  //     art.notice.show = 'Max speed';\n  //   }\n  // });\n  art.hotkey.add(105, () => {\n    // Numpad9\n    art.seek = Number(art.duration) * 0.9;\n  });\n  art.hotkey.add(104, () => {\n    // Numpad8\n    art.seek = Number(art.duration) * 0.8;\n  });\n  art.hotkey.add(103, () => {\n    // Numpad7\n    art.seek = Number(art.duration) * 0.7;\n  });\n  art.hotkey.add(102, () => {\n    // Numpad6\n    art.seek = Number(art.duration) * 0.6;\n  });\n  art.hotkey.add(101, () => {\n    // Numpad5\n    art.seek = Number(art.duration) * 0.5;\n  });\n  art.hotkey.add(100, () => {\n    // Numpad4\n    art.seek = Number(art.duration) * 0.4;\n  });\n  art.hotkey.add(99, () => {\n    // Numpad3\n    art.seek = Number(art.duration) * 0.3;\n  });\n  art.hotkey.add(98, () => {\n    // Numpad2\n    art.seek = Number(art.duration) * 0.2;\n  });\n  art.hotkey.add(97, () => {\n    // Numpad1\n    art.seek = Number(art.duration) * 0.1;\n  });\n  art.hotkey.add(96, () => {\n    // Numpad0\n    art.seek = 0;\n  });\n  art.hotkey.add(77, () => {\n    // Key M\n    art.muted = !art.muted;\n    if (art.muted) {\n      art.notice.show = 'Mute';\n    } else {\n      art.notice.show = 'Unmute';\n    }\n  });\n  art.hotkey.add(76, () => {\n    // Key L\n    art.forward = 10;\n    art.notice.show = 'Forward 10s';\n  });\n  art.hotkey.add(75, () => {\n    // Key K\n    if (art.playing) {\n      art.pause();\n      art.notice.show = 'Pause';\n    } else {\n      art.play();\n      art.notice.show = 'Play';\n    }\n  });\n  art.hotkey.add(74, () => {\n    // Key J\n    art.backward = 10;\n    art.notice.show = 'Backward 10s';\n  });\n  art.hotkey.add(70, () => {\n    // Key F\n    art.fullscreen = !art.fullscreen;\n    if (art.fullscreen) {\n      art.notice.show = 'Fullscreen';\n    } else {\n      art.notice.show = 'Exit fullscreen';\n    }\n  });\n  art.hotkey.add(67, () => {\n    // Key C\n    art.subtitle.show = !art.subtitle.show;\n    setShowSubtitle(art.subtitle.show);\n    if (art.subtitle.show) {\n      art.notice.show = 'Subtitle on';\n    } else {\n      art.notice.show = 'Subtitle off';\n    }\n  });\n  art.hotkey.add(57, () => {\n    // Key 9\n    art.seek = Number(art.duration) * 0.9;\n  });\n  art.hotkey.add(56, () => {\n    // Key 8\n    art.seek = Number(art.duration) * 0.8;\n  });\n  art.hotkey.add(55, () => {\n    // Key 7\n    art.seek = Number(art.duration) * 0.7;\n  });\n  art.hotkey.add(54, () => {\n    // Key 6\n    art.seek = Number(art.duration) * 0.6;\n  });\n  art.hotkey.add(53, () => {\n    // Key 5\n    art.seek = Number(art.duration) * 0.5;\n  });\n  art.hotkey.add(52, () => {\n    // Key 4\n    art.seek = Number(art.duration) * 0.4;\n  });\n  art.hotkey.add(51, () => {\n    // Key 3\n    art.seek = Number(art.duration) * 0.3;\n  });\n  art.hotkey.add(50, () => {\n    // Key 2\n    art.seek = Number(art.duration) * 0.2;\n  });\n  art.hotkey.add(49, () => {\n    // Key 1\n    art.seek = Number(art.duration) * 0.1;\n  });\n  art.hotkey.add(48, () => {\n    // Key 0\n    art.seek = 0;\n  });\n  art.hotkey.add(36, () => {\n    // Key Home\n    art.seek = 0;\n    art.notice.show = 'Seek to start';\n  });\n  art.hotkey.add(35, () => {\n    // Key End\n    art.seek = art.duration;\n    art.notice.show = 'Seek to end';\n  });\n}\n\nexport default PlayerHotKey;\n"
  },
  {
    "path": "app/components/elements/player/PlayerSettings.tsx",
    "content": "import { useMemo, useState } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { Divider } from '@nextui-org/divider';\nimport { Spacer } from '@nextui-org/spacer';\nimport { Switch } from '@nextui-org/switch';\nimport { isMobileOnly } from 'react-device-detect';\n\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport { Dialog, DialogContent, DialogTrigger } from '~/components/elements/Dialog';\nimport AddSubtitles from '~/components/elements/dialog/AddSubtitleDialog';\nimport SearchSubtitles from '~/components/elements/dialog/SearchSubtitleDialog';\nimport { Popover, PopoverContent, PopoverTrigger } from '~/components/elements/Popover';\nimport ResizablePanel from '~/components/elements/shared/ResizablePanel';\nimport { Sheet, SheetContent, SheetTrigger } from '~/components/elements/Sheet';\nimport Arrow from '~/assets/icons/ArrowIcon';\nimport Filter from '~/assets/icons/FilterIcon';\nimport Flip from '~/assets/icons/FlipIcon';\nimport PaperPlus from '~/assets/icons/PaperPlusIcon';\nimport Play from '~/assets/icons/PlayIcon';\nimport Ratio from '~/assets/icons/RatioIcon';\nimport Search from '~/assets/icons/SearchIcon';\nimport Settings from '~/assets/icons/SettingsIcon';\nimport Subtitle from '~/assets/icons/SubtitleIcon';\nimport Tick from '~/assets/icons/TickIcon';\n\ninterface IPlayerSettingsProps {\n  artplayer: Artplayer | null;\n  qualitySelector?: {\n    html: string;\n    url: string;\n    default?: boolean;\n    isM3U8?: boolean;\n    isDASH?: boolean;\n  }[];\n  subtitleSelector?: {\n    html: string;\n    url: string;\n    default?: boolean;\n    type?: string;\n  }[];\n  isPlayerFullScreen?: boolean;\n  isSettingsOpen: boolean;\n  showSubtitle: boolean;\n  setShowSubtitle: React.Dispatch<React.SetStateAction<boolean>>;\n  setSettingsOpen: React.Dispatch<React.SetStateAction<boolean>>;\n  subtitleOptions?: {\n    imdb_id?: number;\n    tmdb_id?: number;\n    parent_feature_id?: number;\n    parent_imdb_id?: number;\n    parent_tmdb_id?: number;\n    episode_number?: number;\n    season_number?: number;\n    type?: 'movie' | 'episode' | 'all';\n    title?: string;\n    sub_format: 'srt' | 'webvtt';\n  };\n}\n\ntype SettingsOption = {\n  id: string;\n  title: string;\n  description?: string;\n  showIcon?: boolean;\n  icon?: JSX.Element;\n  action?: () => void;\n  currentValue?: string;\n  isCurrent?: boolean;\n  isSwitch?: boolean;\n  isSwitchOn?: boolean;\n  isTriggerDialog?: boolean;\n  switchAction?: (isSelected: boolean) => void;\n  dialogName?: string;\n};\n\nconst PlayerSettings = (props: IPlayerSettingsProps) => {\n  const {\n    artplayer,\n    qualitySelector,\n    subtitleSelector,\n    isPlayerFullScreen,\n    isSettingsOpen,\n    showSubtitle,\n    setShowSubtitle,\n    setSettingsOpen,\n    subtitleOptions,\n  } = props;\n\n  const [dropdownLevelKey, setDropdownLevelKey] = useState('general');\n  const [currentPlaySpeed, setCurrentPlaySpeed] = useState('Normal');\n  const [currentAspectRatio, setCurrentAspectRatio] = useState('Default');\n  const [currentVideoFlip, setCurrentVideoFlip] = useState('Normal');\n  const [currentQuality, setCurrentQuality] = useState(\n    qualitySelector?.find((quality) => quality.default === true)?.html || 'Auto',\n  );\n  const [currentSubtitle, setCurrentSubtitle] = useState(\n    subtitleSelector?.find((subtitle) => subtitle.default === true)?.html || 'English',\n  );\n  const [currentSubtitleOffset, setCurrentSubtitleOffset] = useState('Normal');\n  const [openDialog, setOpenDialog] = useState(false);\n  const [currentDialogName, setCurrentDialogName] = useState('');\n\n  const {\n    currentSubtitleFontColor,\n    currentSubtitleFontSize,\n    currentSubtitleBackgroundColor,\n    currentSubtitleBackgroundOpacity,\n    currentSubtitleWindowColor,\n    currentSubtitleWindowOpacity,\n    currentSubtitleTextEffects,\n  } = useSoraSettings();\n\n  const dropdownLevel = useMemo(\n    () => {\n      const level: {\n        id: string;\n        key: string;\n        showTitle: boolean;\n        showBackButton?: boolean;\n        backButtonAction?: () => void;\n        title?: string;\n        showExtraButton?: boolean;\n        extraButtonAction?: () => void;\n        extraButtonTitle?: string;\n        listItems: SettingsOption[];\n      }[] = [\n        {\n          id: 'general',\n          key: 'general',\n          showTitle: false,\n          listItems: [\n            {\n              id: 'play-speed',\n              title: 'Play Speed',\n              description: 'Change the playback speed',\n              showIcon: true,\n              icon: <Play type=\"circle2\" />,\n              action: () => setDropdownLevelKey('play-speed'),\n              currentValue: currentPlaySpeed,\n            },\n            {\n              id: 'aspect-ratio',\n              title: 'Aspect Ratio',\n              description: 'Change the aspect ratio',\n              showIcon: true,\n              icon: <Ratio />,\n              action: () => setDropdownLevelKey('aspect-ratio'),\n              currentValue: currentAspectRatio,\n            },\n            {\n              id: 'video-flip',\n              title: 'Video Flip',\n              description: 'Flip the video horizontal or vertical',\n              showIcon: true,\n              icon: <Flip />,\n              action: () => setDropdownLevelKey('video-flip'),\n              currentValue: currentVideoFlip,\n            },\n            ...(qualitySelector\n              ? [\n                  {\n                    id: 'quality',\n                    title: 'Quality',\n                    description: 'Change the video quality',\n                    showIcon: true,\n                    icon: <Filter />,\n                    action: () => setDropdownLevelKey('quality'),\n                    currentValue: currentQuality,\n                  },\n                ]\n              : []),\n            ...(subtitleSelector\n              ? [\n                  {\n                    id: 'subtitle',\n                    title: 'Subtitle',\n                    description: 'Change the subtitle',\n                    showIcon: true,\n                    icon: <Subtitle filled />,\n                    action: () => setDropdownLevelKey('subtitle'),\n                    currentValue: currentSubtitle,\n                  },\n                ]\n              : []),\n          ],\n        },\n        {\n          id: 'play-speed',\n          key: 'play-speed',\n          showTitle: true,\n          showBackButton: true,\n          backButtonAction: () => setDropdownLevelKey('general'),\n          title: 'Play Speed',\n          listItems: [\n            {\n              id: '0.5x',\n              title: '0.5x',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.playbackRate = 0.5;\n                  setCurrentPlaySpeed('0.5x');\n                  setDropdownLevelKey('general');\n                }\n              },\n              isCurrent: currentPlaySpeed === '0.5x',\n            },\n            {\n              id: '0.75x',\n              title: '0.75x',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.playbackRate = 0.75;\n                  setCurrentPlaySpeed('0.75x');\n                  setDropdownLevelKey('general');\n                }\n              },\n              isCurrent: currentPlaySpeed === '0.75x',\n            },\n            {\n              id: 'normal',\n              title: 'Normal',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.playbackRate = 1.0;\n                  setCurrentPlaySpeed('Normal');\n                  setDropdownLevelKey('general');\n                }\n              },\n              isCurrent: currentPlaySpeed === 'Normal',\n            },\n            {\n              id: '1.25x',\n              title: '1.25x',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.playbackRate = 1.25;\n                  setCurrentPlaySpeed('1.25x');\n                  setDropdownLevelKey('general');\n                }\n              },\n              isCurrent: currentPlaySpeed === '1.25x',\n            },\n            {\n              id: '1.5x',\n              title: '1.5x',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.playbackRate = 1.5;\n                  setCurrentPlaySpeed('1.5x');\n                  setDropdownLevelKey('general');\n                }\n              },\n              isCurrent: currentPlaySpeed === '1.5x',\n            },\n            {\n              id: '1.75x',\n              title: '1.75x',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.playbackRate = 1.75;\n                  setCurrentPlaySpeed('1.75x');\n                  setDropdownLevelKey('general');\n                }\n              },\n              isCurrent: currentPlaySpeed === '1.75x',\n            },\n            {\n              id: '2x',\n              title: '2x',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.playbackRate = 2.0;\n                  setCurrentPlaySpeed('2x');\n                  setDropdownLevelKey('general');\n                }\n              },\n              isCurrent: currentPlaySpeed === '2x',\n            },\n          ],\n        },\n        {\n          id: 'aspect-ratio',\n          key: 'aspect-ratio',\n          showTitle: true,\n          showBackButton: true,\n          backButtonAction: () => setDropdownLevelKey('general'),\n          title: 'Aspect Ratio',\n          listItems: [\n            {\n              id: 'default',\n              title: 'Default',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.aspectRatio = 'default';\n                  setCurrentAspectRatio('Default');\n                  setDropdownLevelKey('general');\n                }\n              },\n              isCurrent: currentAspectRatio === 'Default',\n            },\n            {\n              id: '16:9',\n              title: '16:9',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.aspectRatio = '16:9';\n                  setCurrentAspectRatio('16:9');\n                  setDropdownLevelKey('general');\n                }\n              },\n              isCurrent: currentAspectRatio === '16:9',\n            },\n            {\n              id: '4:3',\n              title: '4:3',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.aspectRatio = '4:3';\n                  setCurrentAspectRatio('4:3');\n                  setDropdownLevelKey('general');\n                }\n              },\n              isCurrent: currentAspectRatio === '4:3',\n            },\n          ],\n        },\n        {\n          id: 'video-flip',\n          key: 'video-flip',\n          showTitle: true,\n          showBackButton: true,\n          backButtonAction: () => setDropdownLevelKey('general'),\n          title: 'Video Flip',\n          listItems: [\n            {\n              id: 'normal',\n              title: 'Normal',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.flip = 'normal';\n                  setCurrentVideoFlip('Normal');\n                  setDropdownLevelKey('general');\n                }\n              },\n              isCurrent: currentVideoFlip === 'Normal',\n            },\n            {\n              id: 'flip-horizontal',\n              title: 'Flip Horizontal',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.flip = 'horizontal';\n                  setCurrentVideoFlip('Horizontal');\n                  setDropdownLevelKey('general');\n                }\n              },\n              isCurrent: currentVideoFlip === 'Horizontal',\n            },\n            {\n              id: 'flip-vertical',\n              title: 'Flip Vertical',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.flip = 'vertical';\n                  setCurrentVideoFlip('Vertical');\n                  setDropdownLevelKey('general');\n                }\n              },\n              isCurrent: currentVideoFlip === 'Vertical',\n            },\n          ],\n        },\n        {\n          id: 'subtitle',\n          key: 'subtitle',\n          showTitle: true,\n          showBackButton: true,\n          backButtonAction: () => setDropdownLevelKey('general'),\n          title: 'Subtitle',\n          showExtraButton: true,\n          extraButtonAction: () => setDropdownLevelKey('subtitle-settings'),\n          extraButtonTitle: 'Subtitle Settings',\n          listItems: [\n            {\n              id: 'toggle-subtitle',\n              title: 'Show Subtitle',\n              showIcon: false,\n              action: undefined,\n              isSwitch: true,\n              isSwitchOn: showSubtitle,\n              switchAction: (isSelected: boolean) => {\n                if (artplayer) {\n                  artplayer.subtitle.show = isSelected;\n                  setShowSubtitle(isSelected);\n                }\n              },\n            },\n            {\n              id: 'search-subtitle',\n              title: 'Search Subtitle',\n              showIcon: true,\n              isTriggerDialog: true,\n              icon: <Search />,\n              action: () => {\n                setSettingsOpen(false);\n                setDropdownLevelKey('general');\n                setOpenDialog(true);\n                setCurrentDialogName('search-subtitle');\n              },\n            },\n            {\n              id: 'add-subtitle',\n              title: 'Add Subtitle',\n              showIcon: true,\n              isTriggerDialog: true,\n              icon: <PaperPlus />,\n              action: () => {\n                setSettingsOpen(false);\n                setDropdownLevelKey('general');\n                setOpenDialog(true);\n                setCurrentDialogName('add-subtitle');\n              },\n            },\n            ...(subtitleSelector\n              ? subtitleSelector.map((subtitle) => ({\n                  id: subtitle.html,\n                  title: subtitle.html,\n                  showIcon: false,\n                  action: () => {\n                    if (artplayer) {\n                      artplayer.subtitle.switch(subtitle.url, {\n                        name: subtitle.html,\n                        ...(subtitle.type ? { type: subtitle.type } : {}),\n                      });\n                      setCurrentSubtitle(subtitle.html);\n                      setDropdownLevelKey('general');\n                    }\n                  },\n                  isCurrent: currentSubtitle === subtitle.html,\n                }))\n              : []),\n          ],\n        },\n        {\n          id: 'subtitle-settings',\n          key: 'subtitle-settings',\n          showTitle: true,\n          showBackButton: true,\n          backButtonAction: () => setDropdownLevelKey('subtitle'),\n          title: 'Subtitle Settings',\n          listItems: [\n            {\n              id: 'subtitle-offset',\n              title: 'Offset',\n              description: 'Change the subtitle offset',\n              showIcon: true,\n              action: () => setDropdownLevelKey('subtitle-offset'),\n              currentValue: currentSubtitleOffset,\n            },\n            {\n              id: 'subtitle-font-color',\n              title: 'Font Color',\n              description: 'Change the subtitle font color',\n              showIcon: true,\n              action: () => setDropdownLevelKey('subtitle-font-color'),\n              currentValue: currentSubtitleFontColor.value,\n            },\n            {\n              id: 'subtitle-font-size',\n              title: 'Font Size',\n              description: 'Change the subtitle font size',\n              showIcon: true,\n              action: () => setDropdownLevelKey('subtitle-font-size'),\n              currentValue: currentSubtitleFontSize.value,\n            },\n            {\n              id: 'subtitle-background-color',\n              title: 'Background Color',\n              description: 'Change the subtitle background color',\n              showIcon: true,\n              action: () => setDropdownLevelKey('subtitle-background-color'),\n              currentValue: currentSubtitleBackgroundColor.value,\n            },\n            {\n              id: 'subtitle-background-opacity',\n              title: 'Background Opacity',\n              description: 'Change the subtitle background opacity',\n              showIcon: true,\n              action: () => setDropdownLevelKey('subtitle-background-opacity'),\n              currentValue: currentSubtitleBackgroundOpacity.value,\n            },\n            {\n              id: 'subtitle-window-color',\n              title: 'Window Color',\n              description: 'Change the subtitle window color',\n              showIcon: true,\n              action: () => setDropdownLevelKey('subtitle-window-color'),\n              currentValue: currentSubtitleWindowColor.value,\n            },\n            {\n              id: 'subtitle-window-opacity',\n              title: 'Window Opacity',\n              description: 'Change the subtitle window opacity',\n              showIcon: true,\n              action: () => setDropdownLevelKey('subtitle-window-opacity'),\n              currentValue: currentSubtitleWindowOpacity.value,\n            },\n            {\n              id: 'subtitle-text-effects',\n              title: 'Text Effects',\n              description: 'Change the subtitle text effects',\n              showIcon: true,\n              action: () => setDropdownLevelKey('subtitle-text-effects'),\n              currentValue: currentSubtitleTextEffects.value,\n            },\n            {\n              id: 'subtitle-reset',\n              title: 'Reset',\n              description: 'Reset the subtitle settings',\n              showIcon: true,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitleOffset = 0;\n                  artplayer.subtitle.style({\n                    color: '#fff',\n                    fontSize: `${artplayer.height * 0.05}px`,\n                  });\n                  setCurrentSubtitleOffset('Normal');\n                  currentSubtitleFontColor.set('White');\n                  currentSubtitleFontSize.set('100%');\n                  currentSubtitleBackgroundColor.set('Black');\n                  currentSubtitleBackgroundOpacity.set('0%');\n                  currentSubtitleWindowColor.set('Black');\n                  currentSubtitleWindowOpacity.set('0%');\n                  setDropdownLevelKey('subtitle');\n                }\n              },\n            },\n          ],\n        },\n        {\n          id: 'subtitle-offset',\n          key: 'subtitle-offset',\n          showTitle: true,\n          showBackButton: true,\n          backButtonAction: () => setDropdownLevelKey('subtitle-settings'),\n          title: 'Subtitle Offset',\n          listItems: [\n            {\n              id: '-5s',\n              title: '-5s',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitleOffset = -5;\n                  setCurrentSubtitleOffset('-5s');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleOffset === '-5s',\n            },\n            {\n              id: '-4s',\n              title: '-4s',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitleOffset = -4;\n                  setCurrentSubtitleOffset('-4s');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleOffset === '-4s',\n            },\n            {\n              id: '-3s',\n              title: '-3s',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitleOffset = -3;\n                  setCurrentSubtitleOffset('-3s');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleOffset === '-3s',\n            },\n            {\n              id: '-2s',\n              title: '-2s',\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitleOffset = -2;\n                  setCurrentSubtitleOffset('-2s');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleOffset === '-2s',\n            },\n            {\n              id: '-1s',\n              title: '-1s',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitleOffset = -1;\n                  setCurrentSubtitleOffset('-1s');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleOffset === '-1s',\n            },\n            {\n              id: 'normal',\n              title: 'Normal',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitleOffset = 0;\n                  setCurrentSubtitleOffset('Normal');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleOffset === 'Normal',\n            },\n            {\n              id: '1s',\n              title: '1s',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitleOffset = 1;\n                  setCurrentSubtitleOffset('1s');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleOffset === '1s',\n            },\n            {\n              id: '2s',\n              title: '2s',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitleOffset = 2;\n                  setCurrentSubtitleOffset('2s');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleOffset === '2s',\n            },\n            {\n              id: '3s',\n              title: '3s',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitleOffset = 3;\n                  setCurrentSubtitleOffset('3s');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleOffset === '3s',\n            },\n            {\n              id: '4s',\n              title: '4s',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitleOffset = 4;\n                  setCurrentSubtitleOffset('4s');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleOffset === '4s',\n            },\n            {\n              id: '5s',\n              title: '5s',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitleOffset = 5;\n                  setCurrentSubtitleOffset('5s');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleOffset === '5s',\n            },\n          ],\n        },\n        {\n          id: 'subtitle-font-color',\n          key: 'subtitle-font-color',\n          showTitle: true,\n          showBackButton: true,\n          backButtonAction: () => setDropdownLevelKey('subtitle-settings'),\n          title: 'Font Color',\n          listItems: [\n            {\n              id: 'white',\n              title: 'White',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitle.style({\n                    color: '#fff',\n                  });\n                  currentSubtitleFontColor.set('White');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleFontColor.value === 'White',\n            },\n            {\n              id: 'blue',\n              title: 'Blue',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitle.style({\n                    color: '#0072F5',\n                  });\n                  currentSubtitleFontColor.set('Blue');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleFontColor.value === 'Blue',\n            },\n            {\n              id: 'purple',\n              title: 'Purple',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitle.style({\n                    color: '#7828C8',\n                  });\n                  currentSubtitleFontColor.set('Purple');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleFontColor.value === 'Purple',\n            },\n            {\n              id: 'green',\n              title: 'Green',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitle.style({\n                    color: '#17C964',\n                  });\n                  currentSubtitleFontColor.set('Green');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleFontColor.value === 'Green',\n            },\n            {\n              id: 'yellow',\n              title: 'Yellow',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitle.style({\n                    color: '#F5A524',\n                  });\n                  currentSubtitleFontColor.set('Yellow');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleFontColor.value === 'Yellow',\n            },\n            {\n              id: 'red',\n              title: 'Red',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitle.style({\n                    color: '#F31260',\n                  });\n                  currentSubtitleFontColor.set('Red');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleFontColor.value === 'Red',\n            },\n            {\n              id: 'cyan',\n              title: 'Cyan',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitle.style({\n                    color: '#06B7DB',\n                  });\n                  currentSubtitleFontColor.set('Cyan');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleFontColor.value === 'Cyan',\n            },\n            {\n              id: 'pink',\n              title: 'Pink',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitle.style({\n                    color: '#FF4ECD',\n                  });\n                  currentSubtitleFontColor.set('Pink');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleFontColor.value === 'Pink',\n            },\n            {\n              id: 'black',\n              title: 'Black',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitle.style({\n                    color: '#000',\n                  });\n                  currentSubtitleFontColor.set('Black');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleFontColor.value === 'Black',\n            },\n          ],\n        },\n        {\n          id: 'subtitle-font-size',\n          key: 'subtitle-font-size',\n          showTitle: true,\n          showBackButton: true,\n          backButtonAction: () => setDropdownLevelKey('subtitle-settings'),\n          title: 'Font Size',\n          listItems: [\n            {\n              id: '50%',\n              title: '50%',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitle.style({\n                    fontSize: `${artplayer.height * 0.05 * 0.5}px`,\n                  });\n                  currentSubtitleFontSize.set('50%');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleFontSize.value === '50%',\n            },\n            {\n              id: '75%',\n              title: '75%',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitle.style({\n                    fontSize: `${artplayer.height * 0.05 * 0.75}px`,\n                  });\n                  currentSubtitleFontSize.set('75%');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleFontSize.value === '75%',\n            },\n            {\n              id: '100%',\n              title: '100%',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitle.style({\n                    fontSize: `${artplayer.height * 0.05}px`,\n                  });\n                  currentSubtitleFontSize.set('100%');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleFontSize.value === '100%',\n            },\n            {\n              id: '125%',\n              title: '125%',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitle.style({\n                    fontSize: `${artplayer.height * 0.05 * 1.25}px`,\n                  });\n                  currentSubtitleFontSize.set('125%');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleFontSize.value === '125%',\n            },\n            {\n              id: '150%',\n              title: '150%',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitle.style({\n                    fontSize: `${artplayer.height * 0.05 * 1.5}px`,\n                  });\n                  currentSubtitleFontSize.set('150%');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleFontSize.value === '150%',\n            },\n            {\n              id: '175%',\n              title: '175%',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitle.style({\n                    fontSize: `${artplayer.height * 0.05 * 1.75}px`,\n                  });\n                  currentSubtitleFontSize.set('175%');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleFontSize.value === '175%',\n            },\n            {\n              id: '200%',\n              title: '200%',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitle.style({\n                    fontSize: `${artplayer.height * 0.05 * 2.0}px`,\n                  });\n                  currentSubtitleFontSize.set('200%');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleFontSize.value === '200%',\n            },\n            {\n              id: '300%',\n              title: '300%',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitle.style({\n                    fontSize: `${artplayer.height * 0.05 * 3.0}px`,\n                  });\n                  currentSubtitleFontSize.set('300%');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleFontSize.value === '300%',\n            },\n            {\n              id: '400%',\n              title: '400%',\n              showIcon: false,\n              action: () => {\n                if (artplayer) {\n                  artplayer.subtitle.style({\n                    fontSize: `${artplayer.height * 0.05 * 4.0}px`,\n                  });\n                  currentSubtitleFontSize.set('400%');\n                  setDropdownLevelKey('subtitle-settings');\n                }\n              },\n              isCurrent: currentSubtitleFontSize.value === '400%',\n            },\n          ],\n        },\n        {\n          id: 'subtitle-background-color',\n          key: 'subtitle-background-color',\n          showTitle: true,\n          showBackButton: true,\n          backButtonAction: () => setDropdownLevelKey('subtitle-settings'),\n          title: 'Background Color',\n          listItems: [\n            {\n              id: 'black',\n              title: 'Black',\n              showIcon: false,\n              action: () => {\n                currentSubtitleBackgroundColor.set('Black');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleBackgroundColor.value === 'Black',\n            },\n            {\n              id: 'blue',\n              title: 'Blue',\n              showIcon: false,\n              action: () => {\n                currentSubtitleBackgroundColor.set('Blue');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleBackgroundColor.value === 'Blue',\n            },\n            {\n              id: 'purple',\n              title: 'Purple',\n              showIcon: false,\n              action: () => {\n                currentSubtitleBackgroundColor.set('Purple');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleBackgroundColor.value === 'Purple',\n            },\n            {\n              id: 'green',\n              title: 'Green',\n              showIcon: false,\n              action: () => {\n                currentSubtitleBackgroundColor.set('Green');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleBackgroundColor.value === 'Green',\n            },\n            {\n              id: 'yellow',\n              title: 'Yellow',\n              showIcon: false,\n              action: () => {\n                currentSubtitleBackgroundColor.set('Yellow');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleBackgroundColor.value === 'Yellow',\n            },\n            {\n              id: 'red',\n              title: 'Red',\n              showIcon: false,\n              action: () => {\n                currentSubtitleBackgroundColor.set('Red');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleBackgroundColor.value === 'Red',\n            },\n            {\n              id: 'cyan',\n              title: 'Cyan',\n              showIcon: false,\n              action: () => {\n                currentSubtitleBackgroundColor.set('Cyan');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleBackgroundColor.value === 'Cyan',\n            },\n            {\n              id: 'pink',\n              title: 'Pink',\n              showIcon: false,\n              action: () => {\n                currentSubtitleBackgroundColor.set('Pink');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleBackgroundColor.value === 'Pink',\n            },\n            {\n              id: 'white',\n              title: 'White',\n              showIcon: false,\n              action: () => {\n                currentSubtitleBackgroundColor.set('White');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleBackgroundColor.value === 'White',\n            },\n          ],\n        },\n        {\n          id: 'subtitle-background-opacity',\n          key: 'subtitle-background-opacity',\n          showTitle: true,\n          showBackButton: true,\n          backButtonAction: () => setDropdownLevelKey('subtitle-settings'),\n          title: 'Background Opacity',\n          listItems: [\n            {\n              id: '0%',\n              title: '0%',\n              showIcon: false,\n              action: () => {\n                currentSubtitleBackgroundOpacity.set('0%');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleBackgroundOpacity.value === '0%',\n            },\n            {\n              id: '25%',\n              title: '25%',\n              showIcon: false,\n              action: () => {\n                currentSubtitleBackgroundOpacity.set('25%');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleBackgroundOpacity.value === '25%',\n            },\n            {\n              id: '50%',\n              title: '50%',\n              showIcon: false,\n              action: () => {\n                currentSubtitleBackgroundOpacity.set('50%');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleBackgroundOpacity.value === '50%',\n            },\n            {\n              id: '75%',\n              title: '75%',\n              showIcon: false,\n              action: () => {\n                currentSubtitleBackgroundOpacity.set('75%');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleBackgroundOpacity.value === '75%',\n            },\n            {\n              id: '100%',\n              title: '100%',\n              showIcon: false,\n              action: () => {\n                currentSubtitleBackgroundOpacity.set('100%');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleBackgroundOpacity.value === '100%',\n            },\n          ],\n        },\n        {\n          id: 'subtitle-window-color',\n          key: 'subtitle-window-color',\n          showTitle: true,\n          showBackButton: true,\n          backButtonAction: () => setDropdownLevelKey('subtitle-settings'),\n          title: 'Window Color',\n          listItems: [\n            {\n              id: 'black',\n              title: 'Black',\n              showIcon: false,\n              action: () => {\n                currentSubtitleWindowColor.set('Black');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleWindowColor.value === 'Black',\n            },\n            {\n              id: 'blue',\n              title: 'Blue',\n              showIcon: false,\n              action: () => {\n                currentSubtitleWindowColor.set('Blue');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleWindowColor.value === 'Blue',\n            },\n            {\n              id: 'purple',\n              title: 'Purple',\n              showIcon: false,\n              action: () => {\n                currentSubtitleWindowColor.set('Purple');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleWindowColor.value === 'Purple',\n            },\n            {\n              id: 'green',\n              title: 'Green',\n              showIcon: false,\n              action: () => {\n                currentSubtitleWindowColor.set('Green');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleWindowColor.value === 'Green',\n            },\n            {\n              id: 'yellow',\n              title: 'Yellow',\n              showIcon: false,\n              action: () => {\n                currentSubtitleWindowColor.set('Yellow');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleWindowColor.value === 'Yellow',\n            },\n            {\n              id: 'red',\n              title: 'Red',\n              showIcon: false,\n              action: () => {\n                currentSubtitleWindowColor.set('Red');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleWindowColor.value === 'Red',\n            },\n            {\n              id: 'cyan',\n              title: 'Cyan',\n              showIcon: false,\n              action: () => {\n                currentSubtitleWindowColor.set('Cyan');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleWindowColor.value === 'Cyan',\n            },\n            {\n              id: 'pink',\n              title: 'Pink',\n              showIcon: false,\n              action: () => {\n                currentSubtitleWindowColor.set('Pink');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleWindowColor.value === 'Pink',\n            },\n            {\n              id: 'white',\n              title: 'White',\n              showIcon: false,\n              action: () => {\n                currentSubtitleWindowColor.set('White');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleWindowColor.value === 'White',\n            },\n          ],\n        },\n        {\n          id: 'subtitle-window-opacity',\n          key: 'subtitle-window-opacity',\n          showTitle: true,\n          showBackButton: true,\n          backButtonAction: () => setDropdownLevelKey('subtitle-settings'),\n          title: 'Window Opacity',\n          listItems: [\n            {\n              id: '0%',\n              title: '0%',\n              showIcon: false,\n              action: () => {\n                currentSubtitleWindowOpacity.set('0%');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleWindowOpacity.value === '0%',\n            },\n            {\n              id: '25%',\n              title: '25%',\n              showIcon: false,\n              action: () => {\n                currentSubtitleWindowOpacity.set('25%');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleWindowOpacity.value === '25%',\n            },\n            {\n              id: '50%',\n              title: '50%',\n              showIcon: false,\n              action: () => {\n                currentSubtitleWindowOpacity.set('50%');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleWindowOpacity.value === '50%',\n            },\n            {\n              id: '75%',\n              title: '75%',\n              showIcon: false,\n              action: () => {\n                currentSubtitleWindowOpacity.set('75%');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleWindowOpacity.value === '75%',\n            },\n            {\n              id: '100%',\n              title: '100%',\n              showIcon: false,\n              action: () => {\n                currentSubtitleWindowOpacity.set('100%');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleWindowOpacity.value === '100%',\n            },\n          ],\n        },\n        {\n          id: 'subtitle-text-effects',\n          key: 'subtitle-text-effects',\n          showTitle: true,\n          showBackButton: true,\n          backButtonAction: () => setDropdownLevelKey('subtitle-settings'),\n          title: 'Text Effects',\n          listItems: [\n            {\n              id: 'none',\n              title: 'None',\n              showIcon: false,\n              action: () => {\n                currentSubtitleTextEffects.set('None');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleTextEffects.value === 'None',\n            },\n            {\n              id: 'drop-shadow',\n              title: 'Drop Shadow',\n              showIcon: false,\n              action: () => {\n                currentSubtitleTextEffects.set('Drop Shadow');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleTextEffects.value === 'Drop Shadow',\n            },\n            {\n              id: 'raised',\n              title: 'Raised',\n              showIcon: false,\n              action: () => {\n                currentSubtitleTextEffects.set('Raised');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleTextEffects.value === 'Raised',\n            },\n            {\n              id: 'depressed',\n              title: 'Depressed',\n              showIcon: false,\n              action: () => {\n                currentSubtitleTextEffects.set('Depressed');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleTextEffects.value === 'Depressed',\n            },\n            {\n              id: 'outline',\n              title: 'Outline',\n              showIcon: false,\n              action: () => {\n                currentSubtitleTextEffects.set('Outline');\n                setDropdownLevelKey('subtitle-settings');\n              },\n              isCurrent: currentSubtitleTextEffects.value === 'Outline',\n            },\n          ],\n        },\n      ];\n      if (qualitySelector) {\n        level.push({\n          id: 'quality',\n          key: 'quality',\n          showTitle: true,\n          showBackButton: true,\n          backButtonAction: () => setDropdownLevelKey('general'),\n          title: 'Quality',\n          listItems: qualitySelector.map((quality) => ({\n            id: quality.html,\n            title: quality.html,\n            showIcon: false,\n            action: async () => {\n              if (artplayer) {\n                await artplayer.switchQuality(quality.url);\n                setCurrentQuality(quality.html);\n                setDropdownLevelKey('general');\n                const currentSubtitleSelected = subtitleSelector?.find(\n                  (subtitle) => subtitle.html === currentSubtitle,\n                );\n                if (currentSubtitleSelected) {\n                  artplayer.subtitle.switch(currentSubtitleSelected.url, {\n                    name: currentSubtitleSelected.html,\n                  });\n                }\n              }\n            },\n            isCurrent: currentQuality === quality.html,\n          })),\n        });\n      }\n      return level;\n    },\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [\n      qualitySelector,\n      subtitleSelector,\n      currentSubtitle,\n      currentQuality,\n      currentPlaySpeed,\n      currentAspectRatio,\n      currentVideoFlip,\n      currentSubtitleOffset,\n      currentSubtitleFontColor.value,\n      currentSubtitleFontSize.value,\n      currentSubtitleBackgroundColor.value,\n      currentSubtitleBackgroundOpacity.value,\n      currentSubtitleWindowColor.value,\n      currentSubtitleWindowOpacity.value,\n      currentSubtitleTextEffects.value,\n    ],\n  );\n  const currentDropdownLevel = useMemo(\n    () => dropdownLevel.find((level) => level.key === dropdownLevelKey),\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [dropdownLevelKey],\n  );\n\n  const containerPortal = useMemo(\n    // portals overlay and content parts into the player when fullscreen is enabled\n    () => (isPlayerFullScreen ? artplayer?.template?.$player : document.body),\n    [artplayer, isPlayerFullScreen],\n  );\n\n  const handleOpenChange = (open: boolean) => {\n    setSettingsOpen(open);\n    if (!open) setDropdownLevelKey('general');\n  };\n\n  const settingsOptions = (item: SettingsOption) => (\n    <Button\n      key={item.id}\n      type=\"button\"\n      fullWidth\n      variant=\"light\"\n      onPress={item.action}\n      className=\"flex h-14 flex-row items-center justify-between gap-x-8 !p-2 data-[hover=true]:bg-default/[.6]\"\n    >\n      <div className=\"flex shrink-0 grow flex-row items-center gap-x-2\">\n        {item?.showIcon ? (\n          (\n            item as {\n              id: string;\n              title: string;\n              description: string;\n              showIcon: boolean;\n              icon: JSX.Element;\n              action: () => void;\n              currentValue: string;\n            }\n          )?.icon\n        ) : (\n            item as {\n              id: string;\n              title: string;\n              showIcon: boolean;\n              action: () => void;\n              isCurrent: boolean;\n            }\n          )?.isCurrent ? (\n          <Tick />\n        ) : (\n          <Spacer x={6} />\n        )}\n        <h6 className=\"!line-clamp-1 !text-default-foreground\">{item.title}</h6>\n      </div>\n      <div className=\"flex shrink-0 grow flex-row items-center justify-end gap-x-2\">\n        {item?.isSwitch ? (\n          <Switch isSelected={showSubtitle} onValueChange={item.switchAction} />\n        ) : (\n          <>\n            <h6 className=\"!text-default-foreground\">\n              {(\n                item as {\n                  id: string;\n                  title: string;\n                  description: string;\n                  showIcon: boolean;\n                  icon: JSX.Element;\n                  action: () => void;\n                  currentValue: string;\n                }\n              )?.currentValue || ''}\n            </h6>\n            {item.showIcon ? <Arrow direction=\"right\" /> : null}\n          </>\n        )}\n      </div>\n    </Button>\n  );\n\n  if (isMobileOnly) {\n    return (\n      <Sheet open={openDialog} onOpenChange={(open) => setOpenDialog(open)}>\n        <Sheet open={isSettingsOpen} onOpenChange={(open) => handleOpenChange(open)}>\n          <SheetTrigger asChild>\n            <Button\n              type=\"button\"\n              size=\"md\"\n              variant=\"light\"\n              isIconOnly\n              aria-label=\"dropdown\"\n              className=\"art-icon data-[hover=true]:bg-transparent\"\n            >\n              <Settings filled />\n            </Button>\n          </SheetTrigger>\n          <SheetContent\n            side=\"bottom\"\n            size=\"content\"\n            hideCloseButton\n            swipeDownToClose\n            open={isSettingsOpen}\n            onOpenChange={() => handleOpenChange(!isSettingsOpen)}\n            container={isPlayerFullScreen ? artplayer?.template?.$player : document.body}\n            className=\"!bg-default\"\n          >\n            <ResizablePanel contentWidth=\"full\">\n              {currentDropdownLevel ? (\n                <div className=\"flex w-full flex-col items-start justify-start gap-y-2 !p-2\">\n                  {currentDropdownLevel?.showBackButton || currentDropdownLevel?.showTitle ? (\n                    <>\n                      <div className=\"flex w-full flex-row items-center justify-between\">\n                        {currentDropdownLevel?.showBackButton ? (\n                          <Button\n                            type=\"button\"\n                            variant=\"light\"\n                            onPress={currentDropdownLevel?.backButtonAction}\n                            startContent={<Arrow direction=\"left\" />}\n                          >\n                            {currentDropdownLevel?.showTitle ? (\n                              <h6 className=\"!text-default-foreground\">\n                                {currentDropdownLevel?.title}\n                              </h6>\n                            ) : null}\n                          </Button>\n                        ) : null}\n                        {currentDropdownLevel?.showExtraButton ? (\n                          <Button\n                            type=\"button\"\n                            variant=\"light\"\n                            onPress={currentDropdownLevel?.extraButtonAction}\n                            className=\"m-0 !p-2 font-bold underline\"\n                          >\n                            {currentDropdownLevel?.extraButtonTitle}\n                          </Button>\n                        ) : null}\n                      </div>\n                      <Divider />\n                    </>\n                  ) : null}\n                  <div className=\"flex w-full flex-col items-start justify-start gap-y-2 !p-2\">\n                    {currentDropdownLevel?.listItems.map((item) => {\n                      if (item.isTriggerDialog) {\n                        return (\n                          <SheetTrigger key={item.id} asChild>\n                            {settingsOptions(item)}\n                          </SheetTrigger>\n                        );\n                      }\n                      return settingsOptions(item);\n                    })}\n                  </div>\n                </div>\n              ) : null}\n            </ResizablePanel>\n          </SheetContent>\n        </Sheet>\n        <SheetContent\n          container={containerPortal}\n          side=\"bottom\"\n          size=\"content\"\n          hideCloseButton\n          swipeDownToClose\n          open={openDialog}\n          onOpenChange={() => setOpenDialog(!openDialog)}\n        >\n          {currentDialogName === 'add-subtitle' ? (\n            <AddSubtitles artplayer={artplayer} setCurrentSubtitle={setCurrentSubtitle} />\n          ) : currentDialogName === 'search-subtitle' ? (\n            <SearchSubtitles\n              artplayer={artplayer}\n              subtitleOptions={subtitleOptions}\n              containerPortal={containerPortal}\n              setCurrentSubtitle={setCurrentSubtitle}\n            />\n          ) : null}\n        </SheetContent>\n      </Sheet>\n    );\n  }\n  return (\n    <Dialog open={openDialog} onOpenChange={(open) => setOpenDialog(open)}>\n      <Popover open={isSettingsOpen} onOpenChange={(open) => handleOpenChange(open)}>\n        <PopoverTrigger asChild>\n          <Button\n            type=\"button\"\n            size=\"md\"\n            variant=\"light\"\n            isIconOnly\n            aria-label=\"dropdown\"\n            className=\"art-icon data-[hover=true]:bg-transparent\"\n          >\n            <Settings filled />\n          </Button>\n        </PopoverTrigger>\n        <PopoverContent\n          side=\"top\"\n          container={containerPortal}\n          className=\"z-[1000] bg-default/60 backdrop-blur-2xl backdrop-contrast-125 backdrop-saturate-200\"\n        >\n          <ResizablePanel contentWidth=\"fit\">\n            {currentDropdownLevel ? (\n              <div className=\"flex w-max flex-col items-start justify-start gap-y-2\">\n                {currentDropdownLevel?.showBackButton || currentDropdownLevel?.showTitle ? (\n                  <>\n                    <div className=\"flex w-full flex-row items-center justify-between gap-x-4\">\n                      {currentDropdownLevel?.showBackButton ? (\n                        <Button\n                          type=\"button\"\n                          variant=\"light\"\n                          onPress={currentDropdownLevel?.backButtonAction}\n                          startContent={<Arrow direction=\"left\" />}\n                        >\n                          {currentDropdownLevel?.showTitle ? (\n                            <h6 className=\"!text-default-foreground\">\n                              {currentDropdownLevel?.title}\n                            </h6>\n                          ) : null}\n                        </Button>\n                      ) : null}\n                      {currentDropdownLevel?.showExtraButton ? (\n                        <Button\n                          type=\"button\"\n                          size=\"md\"\n                          variant=\"light\"\n                          onPress={currentDropdownLevel?.extraButtonAction}\n                          className=\"m-0 !p-2 font-bold underline\"\n                        >\n                          {currentDropdownLevel?.extraButtonTitle}\n                        </Button>\n                      ) : null}\n                    </div>\n                    <Divider />\n                  </>\n                ) : null}\n                <div className=\"flex w-full flex-col items-start justify-start gap-y-2\">\n                  {currentDropdownLevel?.listItems.map((item) => {\n                    if (item.isTriggerDialog) {\n                      return (\n                        <DialogTrigger key={item.id} asChild>\n                          {settingsOptions(item)}\n                        </DialogTrigger>\n                      );\n                    }\n                    return settingsOptions(item);\n                  })}\n                </div>\n              </div>\n            ) : null}\n          </ResizablePanel>\n        </PopoverContent>\n      </Popover>\n      <DialogContent container={containerPortal}>\n        {currentDialogName === 'add-subtitle' ? (\n          <AddSubtitles artplayer={artplayer} setCurrentSubtitle={setCurrentSubtitle} />\n        ) : currentDialogName === 'search-subtitle' ? (\n          <SearchSubtitles\n            artplayer={artplayer}\n            subtitleOptions={subtitleOptions}\n            containerPortal={containerPortal}\n            setCurrentSubtitle={setCurrentSubtitle}\n          />\n        ) : null}\n      </DialogContent>\n    </Dialog>\n  );\n};\n\nexport default PlayerSettings;\n"
  },
  {
    "path": "app/components/elements/player/playerStyles.ts",
    "content": "import { tv } from 'tailwind-variants';\n\nexport const playerStyles = tv({\n  base: [\n    'custom-player-progress-tip',\n    'custom-player-layer-lock',\n    'custom-player-subtitle',\n    'custom-player-layer-auto-playback',\n    'custom-player-contextmenus',\n    'custom-player-info',\n    'custom-player-notice-inner',\n    'custom-player-volume-control',\n    'custom-player-icon-after',\n    'custom-player-icon-before',\n    'custom-player-control-after',\n    'custom-player-control-before',\n    '[&_.art-bottom]:via-default/60',\n    '[&_.art-bottom]:to-default',\n    '[&_.art-contextmenu]:!border-divider',\n    '[&_.art-contextmenu]:!text-shadow-none',\n    '[&_.art-control-topControlButtons]:before:via-default/[0.6]',\n    '[&_.art-control-topControlButtons]:before:to-default',\n    '[&_.art-layer-lock]:bg-background/[0.6]',\n    '[&_.art-subtitle]:bg-player-subtitle-window-color',\n    '[&_.art-subtitle]:!text-shadow-player',\n    '[&_.art-bottom]:!bg-gradient-to-b',\n    '[&_.art-bottom]:from-transparent',\n    '[&_.art-control-topControlButtons]:!opacity-100',\n    '[&_.art-control-topControlButtons]:before:absolute',\n    '[&_.art-control-topControlButtons]:before:left-0',\n    '[&_.art-control-topControlButtons]:before:top-0',\n    '[&_.art-control-topControlButtons]:before:h-[100px]',\n    '[&_.art-control-topControlButtons]:before:w-full',\n    '[&_.art-control-topControlButtons]:before:bg-gradient-to-t',\n    '[&_.art-control-topControlButtons]:before:from-transparent',\n    '[&_.art-control-topControlButtons]:before:bg-top',\n    '[&_.art-control-topControlButtons]:before:bg-repeat-x',\n    \"[&_.art-control-topControlButtons]:before:content-['']\",\n    '[&_.art-layer-mask]:hidden',\n    '[&_.art-layer-mask]:bg-transparent',\n    '[&_.art-layer-mask]:transition-all',\n    '[&_.art-layer-mask]:duration-300',\n    '[&_.art-layer-mask]:ease-in-out',\n    '[&_.art-layer-miniTopControlButtons]:hidden',\n    '[&_.art-layer-miniTopControlButtons]:transition-all',\n    '[&_.art-layer-miniTopControlButtons]:duration-300',\n    '[&_.art-layer-miniTopControlButtons]:ease-in-out',\n    '[&_.art-layer-playPauseButton]:hidden',\n    '[&_.art-layer-playPauseButton]:transition-all',\n    '[&_.art-layer-playPauseButton]:duration-300',\n    '[&_.art-layer-playPauseButton]:ease-in-out',\n    '[&_.art-notice]:!justify-center',\n    '[&_.art-video-player]:!font-sans',\n  ],\n  variants: {\n    isMini: {\n      true: [\n        'custom-mini-player-hover',\n        'h-[14.0625rem]',\n        'w-[25rem]',\n        'rounded-t-medium',\n        '[&_.art-bottom]:!visible',\n        '[&_.art-bottom]:!overflow-visible',\n        '[&_.art-bottom]:!bg-none',\n        '[&_.art-bottom]:!p-0',\n        '[&_.art-bottom]:!opacity-100',\n        '[&_.art-control-progress]:!h-[7px]',\n        '[&_.art-control-progress]:!items-end',\n        '[&_.art-controls]:hidden',\n        '[&_.art-controls]:!transform-none',\n        '[&_.art-mask]:!hidden',\n        '[&_.art-progress]:!transform-none',\n        '[&_.art-subtitle]:!bottom-[7px]',\n      ],\n      false: 'h-full w-full rounded-none',\n    },\n    isMobile: {\n      true: [\n        '[&_.art-bottom]:!p-0',\n        '[&_.art-controls]:!px-[10px]',\n        '[&_.art-progress-indicator]:!m-0',\n      ],\n      false: '',\n    },\n    isShowOverlay: {\n      true: '[&_.art-video-player]:!z-[9999]',\n      false: '',\n    },\n    isSettingsOpen: {\n      true: '',\n      false: '',\n    },\n    isPlayerFullScreen: {\n      true: [\n        '[&_.art-control-topControlButtons]:block',\n        '[&_.art-control-topControlButtons]:before:block',\n      ],\n      false: [\n        '[&_.art-control-topControlButtons]:hidden',\n        '[&_.art-control-topControlButtons]:before:hidden',\n      ],\n    },\n    showSubtitle: {\n      true: '[&_.art-subtitle]:!flex',\n      false: '[&_.art-subtitle]:!hidden',\n    },\n  },\n  compoundVariants: [\n    {\n      isMini: true,\n      isSettingsOpen: true,\n      class: [\n        '[&_.art-layer-mask]:bg-background/[0.6]',\n        '[&_.art-layer-mask]:block',\n        '[&_.art-layer-miniTopControlButtons]:block',\n      ],\n    },\n    {\n      isMobile: true,\n      isPlayerFullScreen: false,\n      class: [\n        '[&_.art-bottom]:!flex-col-reverse',\n        '[&_.art-bottom]:!justify-start',\n        '[&_.art-bottom]:!overflow-visible',\n      ],\n    },\n  ],\n});\n"
  },
  {
    "path": "app/components/elements/shared/AuthForm.tsx",
    "content": "import { useMemo, useState } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { Card, CardBody, CardFooter, CardHeader } from '@nextui-org/card';\nimport { Checkbox } from '@nextui-org/checkbox';\nimport { Input } from '@nextui-org/input';\nimport { Link } from '@nextui-org/link';\nimport { EyeFilledIcon, EyeSlashFilledIcon } from '@nextui-org/shared-icons';\nimport { Spacer } from '@nextui-org/spacer';\nimport { Form, Link as RemixLink, useLocation } from '@remix-run/react';\nimport { useTranslation } from 'react-i18next';\n\nimport MailEdit from '~/assets/icons/MailEditIcon';\nimport Password from '~/assets/icons/Password';\n\ninterface IAuthForm {\n  type: 'sign-in' | 'sign-up';\n  error?: string | null;\n  errorCode?: string | null;\n  code?: string | null;\n}\n\nconst AuthForm = ({ type, error, code, errorCode }: IAuthForm) => {\n  const { t } = useTranslation('auth');\n  const location = useLocation();\n\n  const hasMessage = code === '201-email';\n  const inviteCode = new URLSearchParams(location.search).get('code') ?? '';\n  const [isPasswordVisible, setIsPasswordVisible] = useState(false);\n  const [isRePasswordVisible, setIsRePasswordVisible] = useState(false);\n  const [value, setValue] = useState<string | undefined>('');\n\n  const validateEmail = (text: string) => text.match(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}$/i);\n  const validationState = useMemo(() => {\n    if (value === '') return undefined;\n\n    return validateEmail(value ?? '') ? 'valid' : 'invalid';\n  }, [value]);\n  const togglePasswordVisibility = () => setIsPasswordVisible(!isPasswordVisible);\n  const toggleRePasswordVisibility = () => setIsRePasswordVisible(!isRePasswordVisible);\n\n  return (\n    <Form method=\"post\" className=\"flex w-full justify-center\">\n      <Card className=\"w-full max-w-lg\">\n        <CardHeader className=\"flex w-full justify-center\">\n          <h2 className=\"bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent\">\n            {t('welcome')}\n          </h2>\n        </CardHeader>\n        <CardBody className=\"flex flex-col gap-y-3 py-4\">\n          <Input\n            value={value}\n            onValueChange={setValue}\n            onClear={() => setValue('')}\n            color={\n              validationState === 'invalid'\n                ? 'danger'\n                : validationState === 'valid'\n                ? 'success'\n                : 'default'\n            }\n            name=\"email\"\n            type=\"email\"\n            variant=\"faded\"\n            fullWidth\n            size=\"lg\"\n            placeholder={t('email')}\n            aria-label=\"Email\"\n            startContent={<MailEdit fill=\"currentColor\" />}\n            errorMessage={validationState === 'invalid' && 'Please enter a valid email'}\n            validationState={validationState}\n          />\n          <Spacer y={2.5} />\n          <Input\n            endContent={\n              <button\n                className=\"focus:outline-none\"\n                type=\"button\"\n                onClick={togglePasswordVisibility}\n              >\n                {isPasswordVisible ? (\n                  <EyeSlashFilledIcon className=\"pointer-events-none text-2xl text-default-400\" />\n                ) : (\n                  <EyeFilledIcon className=\"pointer-events-none text-2xl text-default-400\" />\n                )}\n              </button>\n            }\n            type={isPasswordVisible ? 'text' : 'password'}\n            name=\"password\"\n            variant=\"faded\"\n            fullWidth\n            color=\"default\"\n            size=\"lg\"\n            placeholder={t('password')}\n            aria-label=\"Password\"\n            startContent={<Password fill=\"currentColor\" />}\n          />\n          {type === 'sign-up' ? (\n            <>\n              <Spacer y={2.5} />\n              <Input\n                endContent={\n                  <button\n                    className=\"focus:outline-none\"\n                    type=\"button\"\n                    onClick={toggleRePasswordVisibility}\n                  >\n                    {isRePasswordVisible ? (\n                      <EyeSlashFilledIcon className=\"pointer-events-none text-2xl text-default-400\" />\n                    ) : (\n                      <EyeFilledIcon className=\"pointer-events-none text-2xl text-default-400\" />\n                    )}\n                  </button>\n                }\n                type={isRePasswordVisible ? 'text' : 'password'}\n                name=\"re-password\"\n                variant=\"faded\"\n                fullWidth\n                color=\"default\"\n                size=\"lg\"\n                placeholder={t('confirmPwd')}\n                aria-label=\"Confirm Password\"\n                startContent={<Password fill=\"currentColor\" />}\n              />\n              <input type=\"hidden\" name=\"invite-code\" hidden value={inviteCode} />\n            </>\n          ) : null}\n          {error ? <h6 className=\"text-danger\">{error}</h6> : null}\n          {errorCode ? <h6 className=\"text-danger\">{t(errorCode)}</h6> : null}\n          {!error && hasMessage ? <h6 className=\"text-success\">{t(code)}</h6> : null}\n          <div className=\"flex items-center justify-between\">\n            {type === 'sign-in' ? (\n              <>\n                <Link\n                  as={RemixLink}\n                  to=\"/sign-up\"\n                  underline=\"hover\"\n                  size=\"lg\"\n                  className=\"font-semibold text-primary focus:outline-none focus:ring-2 focus:ring-focus\"\n                >\n                  {t('signUp')}\n                </Link>\n                <Checkbox size=\"lg\">{t('rememberMe')}</Checkbox>\n              </>\n            ) : (\n              <Link\n                as={RemixLink}\n                to=\"/sign-in\"\n                underline=\"hover\"\n                size=\"lg\"\n                className=\"font-semibold text-primary focus:outline-none focus:ring-2 focus:ring-focus\"\n              >\n                {t('signIn')}\n              </Link>\n            )}\n          </div>\n        </CardBody>\n        <CardFooter className=\"flex justify-end p-5\">\n          <Button color=\"primary\" type=\"submit\" size=\"lg\">\n            {type === 'sign-in' ? t('signIn') : t('signUp')}\n          </Button>\n        </CardFooter>\n      </Card>\n    </Form>\n  );\n};\n\nexport default AuthForm;\n"
  },
  {
    "path": "app/components/elements/shared/ErrorBoundaryView.tsx",
    "content": "import { Image } from '@nextui-org/image';\nimport { isRouteErrorResponse, useParams, useRouteError } from '@remix-run/react';\nimport { type ErrorResponse } from '@remix-run/router';\nimport { getErrorMessage } from '~/utils';\n\nimport errorGif from '~/assets/images/404.gif';\n\ntype StatusHandler = (info: {\n  error: ErrorResponse;\n  params: Record<string, string | undefined>;\n}) => JSX.Element | null;\n\nfunction ErrorBoundary(props: {\n  defaultStatusHandler?: StatusHandler;\n  statusHandlers?: Record<number, StatusHandler>;\n  unexpectedErrorHandler?: (error: unknown) => JSX.Element | null;\n}) {\n  const {\n    defaultStatusHandler = ({ error }) => {\n      let message;\n      switch (error.status) {\n        case 401:\n          message = (\n            <p>Oops! Looks like you tried to visit a page that you do not have access to.</p>\n          );\n          break;\n        case 404:\n          message = <p>Oops! Looks like you tried to visit a page that does not exist.</p>;\n          break;\n        default:\n          throw new Error(error.data || error.statusText);\n      }\n      return <p>{message}</p>;\n    },\n    statusHandlers,\n    unexpectedErrorHandler = (error) => {\n      const isProd = process.env.NODE_ENV === 'production';\n      return (\n        <>\n          <h1 className=\"text-center text-danger\">Some thing went wrong</h1>\n          {!isProd ? <p>{getErrorMessage(error)}</p> : null}\n        </>\n      );\n    },\n  } = props;\n  const error = useRouteError();\n  const params = useParams();\n\n  if (typeof document !== 'undefined') {\n    // eslint-disable-next-line no-console\n    console.error(error);\n  }\n\n  return (\n    <div className=\"text-h2 container mx-auto flex items-center justify-center p-20\">\n      <div className=\"mt-32 flex flex-col items-center justify-center gap-y-4\">\n        <Image width={480} src={errorGif} alt=\"404\" className=\"object-cover\" />\n        {isRouteErrorResponse(error) ? (\n          <>\n            <h1 className=\"text-center text-warning\">\n              {error.status} {error.statusText}\n            </h1>\n            {(statusHandlers?.[error.status] ?? defaultStatusHandler)({\n              error,\n              params,\n            })}\n          </>\n        ) : (\n          unexpectedErrorHandler(error)\n        )}\n      </div>\n    </div>\n  );\n}\n\nexport default ErrorBoundary;\n"
  },
  {
    "path": "app/components/elements/shared/Filter.tsx",
    "content": "import { useMemo, useState } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { Input } from '@nextui-org/input';\nimport { Tooltip } from '@nextui-org/tooltip';\nimport { Form, useSearchParams } from '@remix-run/react';\nimport { useTranslation } from 'react-i18next';\n\nimport type { ILanguage } from '~/services/tmdb/tmdb.types';\nimport {\n  animeFormat,\n  animeGenres,\n  animeSeason,\n  animeStatus,\n  tvStatus,\n} from '~/constants/filterItems';\nimport {\n  ScrollArea,\n  ScrollBar,\n  ScrollCorner,\n  ScrollViewport,\n} from '~/components/elements/ScrollArea';\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '~/components/elements/Select';\nimport { SheetFooter } from '~/components/elements/Sheet';\nimport Slider from '~/components/elements/Slider';\n\ninterface IFilterProps {\n  /**\n   * genres for movies and tv series\n   */\n  genres?: { [id: string]: string };\n  mediaType?: 'movie' | 'tv' | 'anime';\n  languages?: ILanguage[];\n}\n\nconst Filter: React.FC<IFilterProps> = (props: IFilterProps) => {\n  const { genres, mediaType, languages } = props;\n  const { t } = useTranslation();\n  const [searchParams, setSearchParams] = useSearchParams();\n\n  const currentYear = new Date().getFullYear();\n  const languageItems = useMemo(() => {\n    const languagesSorted =\n      languages?.sort((a, b) => {\n        const textA = a.english_name.toUpperCase();\n        const textB = b.english_name.toUpperCase();\n        return textA < textB ? -1 : textA > textB ? 1 : 0;\n      }) || [];\n    return [{ iso_639_1: 'All', english_name: t('all'), name: t('all') }, ...languagesSorted];\n  }, [languages, t]);\n  const tvStatusItems: typeof tvStatus = {\n    All: t('all'),\n    ...tvStatus,\n  };\n  const animeYearItems = [\n    t('all'),\n    ...Array.from(new Array(currentYear - 1938), (_, i) => (i + 1940).toString()).reverse(),\n  ];\n  const animeSeasonItems = [t('all'), ...animeSeason];\n  const animeFormatItems = [t('all'), ...animeFormat];\n  const animeAiringStatusItems = [t('all'), ...animeStatus];\n  const currentSearchParams = useMemo<{ [key: string]: string }>(() => {\n    const params: { [key: string]: string } = {};\n    searchParams.forEach((value, key) => {\n      params[key] = value;\n    });\n    return params;\n  }, [searchParams]);\n  const [genresSelected, setGenresSelected] = useState<string[]>(\n    currentSearchParams?.with_genres?.split(',') || [],\n  );\n  const [langSelected, setLangSelected] = useState<string>(\n    currentSearchParams?.with_original_language || 'All',\n  );\n  const [tvStatusSelected, setTvStatusSelected] = useState<string>(\n    currentSearchParams?.with_status || 'All',\n  );\n  const [voteCountSelected, setVoteCountSelected] = useState<number[]>(\n    currentSearchParams['vote_count.gte'] ? [Number(currentSearchParams['vote_count.gte'])] : [200],\n  );\n  const [userScoreSelected, setUserScoreSelected] = useState<number[]>(() => {\n    const preVoteAverageGte = currentSearchParams['vote_average.gte'];\n    const preVoteAverageLte = currentSearchParams['vote_average.lte'];\n    if (preVoteAverageGte || preVoteAverageLte) {\n      return [Number(preVoteAverageGte || 0), Number(preVoteAverageLte || 10)];\n    }\n    return [0, 10];\n  });\n  const [runtimeSelected, setRuntimeSelected] = useState<number[]>(() => {\n    const preRuntimeGte = currentSearchParams['with_runtime.gte'];\n    const preRuntimeLte = currentSearchParams['with_runtime.lte'];\n    if (preRuntimeGte || preRuntimeLte) {\n      return [Number(preRuntimeGte || 0), Number(preRuntimeLte || 400)];\n    }\n    return [0, 400];\n  });\n  const [animeGenresSelected, setAnimeGenresSelected] = useState<string[]>(\n    currentSearchParams?.genres?.split(',') || [],\n  );\n  const [animeYearSelected, setAnimeYearSelected] = useState<string>(\n    currentSearchParams?.year || t('all'),\n  );\n  const [animeSeasonSelected, setAnimeSeasonSelected] = useState<string>(\n    currentSearchParams?.season || t('all'),\n  );\n  const [animeFormatSelected, setAnimeFormatSelected] = useState<string>(\n    currentSearchParams?.format || t('all'),\n  );\n  const [animeAiringStatusSelected, setAnimeAiringStatusSelected] = useState<string>(\n    currentSearchParams?.status || t('all'),\n  );\n  const [animeQueryInput, setAnimeQueryInput] = useState<string | undefined>(\n    currentSearchParams?.query || '',\n  );\n\n  const handleGenrePress = (genreId: string) => {\n    setGenresSelected((prev) => {\n      if (prev?.includes(genreId)) {\n        return prev.filter((id) => id !== genreId);\n      }\n      return [...(prev || []), genreId];\n    });\n  };\n  const handleAnimeGenrePress = (genreId: string) => {\n    setAnimeGenresSelected((prev) => {\n      if (prev?.includes(genreId)) {\n        return prev.filter((id) => id !== genreId);\n      }\n      return [...(prev || []), genreId];\n    });\n  };\n  const handleReset = () => {\n    setGenresSelected([]);\n    setAnimeGenresSelected([]);\n    setLangSelected('All');\n    setTvStatusSelected('All');\n    setVoteCountSelected([200]);\n    setUserScoreSelected([0, 10]);\n    setRuntimeSelected([0, 400]);\n    setAnimeYearSelected('All');\n    setAnimeSeasonSelected('All');\n    setAnimeFormatSelected('All');\n    setAnimeAiringStatusSelected('All');\n    setAnimeQueryInput('');\n  };\n  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {\n    e.preventDefault();\n    let params: { [key: string]: string } = {};\n    if (mediaType === 'movie' || mediaType === 'tv') {\n      params = {\n        ...(currentSearchParams?.sort_by ? { sort_by: currentSearchParams?.sort_by } : {}),\n        ...(genresSelected && genresSelected.length > 0\n          ? { with_genres: genresSelected?.join(',') }\n          : {}),\n        ...(langSelected && langSelected !== 'All' ? { with_original_language: langSelected } : {}),\n        ...(tvStatusSelected && tvStatusSelected !== 'All'\n          ? { with_status: tvStatusSelected }\n          : {}),\n        ...(voteCountSelected && voteCountSelected[0] !== 200\n          ? { 'vote_count.gte': voteCountSelected[0].toString() }\n          : {}),\n        ...(userScoreSelected && userScoreSelected[0] !== 0\n          ? { 'vote_average.gte': userScoreSelected[0].toString() }\n          : {}),\n        ...(userScoreSelected && userScoreSelected[1] !== 10\n          ? { 'vote_average.lte': userScoreSelected[1].toString() }\n          : {}),\n        ...(runtimeSelected && runtimeSelected[0] !== 0\n          ? { 'with_runtime.gte': runtimeSelected[0].toString() }\n          : {}),\n        ...(runtimeSelected && runtimeSelected[1] !== 400\n          ? { 'with_runtime.lte': runtimeSelected[1].toString() }\n          : {}),\n      };\n    }\n    if (mediaType === 'anime') {\n      params = {\n        ...(currentSearchParams?.sort ? { sort: currentSearchParams?.sort } : {}),\n        ...(animeGenresSelected && animeGenresSelected.length > 0\n          ? { genres: animeGenresSelected?.join(',') }\n          : {}),\n        ...(animeYearSelected && animeYearSelected !== t('all') ? { year: animeYearSelected } : {}),\n        ...(animeSeasonSelected && animeSeasonSelected !== t('all')\n          ? { season: animeSeasonSelected }\n          : {}),\n        ...(animeFormatSelected && animeFormatSelected !== t('all')\n          ? { format: animeFormatSelected }\n          : {}),\n        ...(animeAiringStatusSelected && animeAiringStatusSelected !== t('all')\n          ? { status: animeAiringStatusSelected }\n          : {}),\n        ...(animeQueryInput && animeQueryInput !== '' ? { query: animeQueryInput } : {}),\n      };\n    }\n    setSearchParams(params);\n  };\n  return (\n    <Form className=\"flex w-full flex-col gap-y-6\" onSubmit={handleSubmit}>\n      <ScrollArea type=\"always\" className=\"h-[calc(83vh-200px)] w-full md:h-[calc(100vh-180px)]\">\n        <ScrollViewport>\n          <div className=\"flex w-full flex-col items-start justify-center gap-y-6 px-3 md:px-6\">\n            {mediaType === 'movie' || mediaType === 'tv' ? (\n              <>\n                <div className=\"flex w-full flex-col items-start justify-start gap-3\">\n                  <h6>{t('genre')}</h6>\n                  {genres ? (\n                    <div className=\"flex w-full flex-row flex-wrap gap-2\">\n                      {Object.keys(genres).map((id) => (\n                        <Button\n                          key={id}\n                          color={genresSelected?.includes(id) ? 'primary' : 'default'}\n                          variant={genresSelected?.includes(id) ? 'flat' : 'solid'}\n                          onPress={() => handleGenrePress(id)}\n                        >\n                          {genres[id]}\n                        </Button>\n                      ))}\n                    </div>\n                  ) : null}\n                </div>\n                <div className=\"flex w-full flex-col items-start justify-start gap-3\">\n                  <h6>{t('language')}</h6>\n                  {languageItems && languageItems.length > 0 ? (\n                    <Select value={langSelected} onValueChange={setLangSelected}>\n                      <SelectTrigger aria-label=\"language\">\n                        <SelectValue placeholder={t('language')} />\n                      </SelectTrigger>\n                      <SelectContent>\n                        {languageItems.map((lang) => (\n                          <SelectItem key={lang.iso_639_1} value={lang.iso_639_1}>\n                            {lang.english_name}\n                          </SelectItem>\n                        ))}\n                      </SelectContent>\n                    </Select>\n                  ) : null}\n                </div>\n                {mediaType === 'tv' ? (\n                  <div className=\"flex w-full flex-col items-start justify-start gap-3\">\n                    <h6>{t('status')}</h6>\n                    {tvStatusItems ? (\n                      <Select value={tvStatusSelected} onValueChange={setTvStatusSelected}>\n                        <SelectTrigger aria-label=\"status\">\n                          <SelectValue placeholder={t('status')} />\n                        </SelectTrigger>\n                        <SelectContent>\n                          {Object.keys(tvStatusItems).map((id) => (\n                            <SelectItem key={id} value={id}>\n                              {tvStatusItems[id]}\n                            </SelectItem>\n                          ))}\n                        </SelectContent>\n                      </Select>\n                    ) : null}\n                  </div>\n                ) : null}\n                <div className=\"flex w-full flex-col items-start justify-start gap-3\">\n                  <div className=\"flex w-full flex-row items-center justify-between\">\n                    <h6>{t('minimum-user-votes')}</h6>\n                    <p className=\"text-default-foreground/80\">{voteCountSelected[0]}</p>\n                  </div>\n                  <Slider\n                    defaultValue={voteCountSelected}\n                    value={voteCountSelected}\n                    name=\"Minimum user votes\"\n                    onValueChange={setVoteCountSelected}\n                    min={0}\n                    max={500}\n                    step={50}\n                    color=\"gradient\"\n                    showValueOnHover\n                  />\n                </div>\n                <div className=\"flex w-full flex-col items-start justify-start gap-3\">\n                  <div className=\"flex w-full flex-row items-center justify-between\">\n                    <h6>{t('user-score')}</h6>\n                    <p className=\"text-default-foreground/80\">\n                      {t('rated')} {userScoreSelected[0]} - {userScoreSelected[1]}\n                    </p>\n                  </div>\n                  <Slider\n                    defaultValue={userScoreSelected}\n                    value={userScoreSelected}\n                    name=\"User score\"\n                    onValueChange={setUserScoreSelected}\n                    min={0}\n                    max={10}\n                    step={0.5}\n                    color=\"gradient\"\n                    showValueOnHover\n                  />\n                </div>\n                <div className=\"flex w-full flex-col items-start justify-start gap-3\">\n                  <div className=\"flex w-full flex-row items-center justify-between\">\n                    <h6>{t('runtime')}</h6>\n                    <p className=\"text-default-foreground/80\">\n                      {runtimeSelected[0]} {t('minutes')} - {runtimeSelected[1]} {t('minutes')}\n                    </p>\n                  </div>\n                  <Slider\n                    defaultValue={runtimeSelected}\n                    value={runtimeSelected}\n                    name=\"Runtime\"\n                    onValueChange={setRuntimeSelected}\n                    min={0}\n                    max={400}\n                    step={15}\n                    color=\"gradient\"\n                    showValueOnHover\n                  />\n                </div>\n              </>\n            ) : null}\n            {mediaType === 'anime' ? (\n              <>\n                <div className=\"flex w-full flex-col items-start justify-start gap-3\">\n                  <h6>{t('search.title.anime')}</h6>\n                  <Input\n                    value={animeQueryInput}\n                    onValueChange={setAnimeQueryInput}\n                    onClear={() => setAnimeQueryInput('')}\n                    variant=\"faded\"\n                    description={t('search.helper.anime')}\n                    type=\"text\"\n                    fullWidth\n                    labelPlacement=\"outside\"\n                  />\n                </div>\n                <div className=\"flex w-full flex-col items-start justify-start gap-3\">\n                  <h6>{t('genre')}</h6>\n                  {animeGenres ? (\n                    <div className=\"flex w-full flex-row flex-wrap gap-2\">\n                      {animeGenres.map((genre) => (\n                        <Button\n                          key={genre}\n                          color={animeGenresSelected?.includes(genre) ? 'primary' : 'default'}\n                          variant={animeGenresSelected?.includes(genre) ? 'flat' : 'solid'}\n                          onPress={() => handleAnimeGenrePress(genre)}\n                        >\n                          {genre}\n                        </Button>\n                      ))}\n                    </div>\n                  ) : null}\n                </div>\n                <div className=\"flex w-full flex-col items-start justify-start gap-3\">\n                  <h6>{t('year')}</h6>\n                  {animeYearItems ? (\n                    <Select value={animeYearSelected} onValueChange={setAnimeYearSelected}>\n                      <SelectTrigger aria-label=\"year\">\n                        <SelectValue placeholder={t('year')} />\n                      </SelectTrigger>\n                      <SelectContent>\n                        {animeYearItems.map((year) => (\n                          <SelectItem key={year} value={year}>\n                            {year}\n                          </SelectItem>\n                        ))}\n                      </SelectContent>\n                    </Select>\n                  ) : null}\n                </div>\n                <div className=\"flex w-full flex-col items-start justify-start gap-3\">\n                  <h6>{t('season')}</h6>\n                  {animeSeasonItems ? (\n                    <Select value={animeSeasonSelected} onValueChange={setAnimeSeasonSelected}>\n                      <SelectTrigger aria-label=\"season\">\n                        <SelectValue placeholder={t('season')} />\n                      </SelectTrigger>\n                      <SelectContent>\n                        {animeSeasonItems.map((season) => (\n                          <SelectItem key={season} value={season}>\n                            {season}\n                          </SelectItem>\n                        ))}\n                      </SelectContent>\n                    </Select>\n                  ) : null}\n                </div>\n                <div className=\"flex w-full flex-col items-start justify-start gap-3\">\n                  <h6>{t('format')}</h6>\n                  {animeFormatItems ? (\n                    <Select value={animeFormatSelected} onValueChange={setAnimeFormatSelected}>\n                      <SelectTrigger aria-label=\"format\">\n                        <SelectValue placeholder={t('format')} />\n                      </SelectTrigger>\n                      <SelectContent>\n                        {animeFormatItems.map((format) => (\n                          <SelectItem key={format} value={format}>\n                            {format}\n                          </SelectItem>\n                        ))}\n                      </SelectContent>\n                    </Select>\n                  ) : null}\n                </div>\n                <div className=\"flex w-full flex-col items-start justify-start gap-3\">\n                  <h6>{t('airing-status')}</h6>\n                  {animeAiringStatusItems ? (\n                    <Select\n                      value={animeAiringStatusSelected}\n                      onValueChange={setAnimeAiringStatusSelected}\n                    >\n                      <SelectTrigger aria-label=\"Airing status\">\n                        <SelectValue placeholder={t('airing-status')} />\n                      </SelectTrigger>\n                      <SelectContent>\n                        {animeAiringStatusItems.map((status) => (\n                          <SelectItem key={status} value={status}>\n                            {status}\n                          </SelectItem>\n                        ))}\n                      </SelectContent>\n                    </Select>\n                  ) : null}\n                </div>\n              </>\n            ) : null}\n          </div>\n        </ScrollViewport>\n        <ScrollBar />\n        <ScrollCorner />\n      </ScrollArea>\n      <SheetFooter className=\"px-1 md:px-6\">\n        <Tooltip content={t('reset-tooltip')} showArrow closeDelay={0}>\n          <Button color=\"default\" type=\"button\" onPress={() => handleReset()}>\n            {t('reset')}\n          </Button>\n        </Tooltip>\n        <Button color=\"primary\" type=\"submit\">\n          {t('discover')}\n        </Button>\n      </SheetFooter>\n    </Form>\n  );\n};\n\nexport default Filter;\n"
  },
  {
    "path": "app/components/elements/shared/ListEpisodes.tsx",
    "content": "import { useMemo, useState } from 'react';\nimport { Button, ButtonGroup } from '@nextui-org/button';\nimport { Card, CardBody } from '@nextui-org/card';\nimport { Pagination } from '@nextui-org/pagination';\nimport { Spacer } from '@nextui-org/spacer';\nimport { useMediaQuery } from '@react-hookz/web';\nimport { useNavigate } from '@remix-run/react';\nimport { useTranslation } from 'react-i18next';\nimport { MimeType } from 'remix-image';\n\nimport type { IEpisodeInfo } from '~/services/consumet/anilist/anilist.types';\nimport type { IEpisode } from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport useSplitArrayIntoPage from '~/utils/react/hooks/useSplitArrayIntoPage';\nimport episodeTypes from '~/constants/episodeTypes';\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '~/components/elements/Select';\nimport Rating from '~/components/elements/shared/Rating';\nimport PhotoIcon from '~/assets/icons/PhotoIcon';\nimport ViewGrid from '~/assets/icons/ViewGridCardIcon';\n\nimport Image from '../Image';\n\ninterface IListEpisodesProps {\n  type: 'tv' | 'anime';\n  id: number | string | undefined;\n  episodes?: IEpisode[] | IEpisodeInfo[];\n  season?: number;\n  providers: {\n    id?: string | number | null;\n    provider: string;\n    episodesCount?: number;\n  }[];\n}\n\nconst ListEpisodes: React.FC<IListEpisodesProps> = (props: IListEpisodesProps) => {\n  const { type, id, episodes, season, providers } = props;\n  const navigate = useNavigate();\n  const { t } = useTranslation();\n  const { isShowSkipOpEdButton } = useSoraSettings();\n  const episodesCountAvailable = useMemo(() => episodes && episodes.length, [episodes]);\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const [selectedProvider, setSelectedProvider] = useState(providers[0]?.provider);\n  const [episodesCountProvider, setEpisodesCountProvider] = useState<number>(\n    providers[0]?.episodesCount || 0,\n  );\n  const [episodesAvailable, setEpisodesAvailable] = useState<\n    IEpisode[] | IEpisodeInfo[] | number[] | undefined\n  >(\n    episodesCountAvailable && episodesCountAvailable >= (providers[0]?.episodesCount || 0)\n      ? episodes?.slice(0, providers[0]?.episodesCount || 0)\n      : Array(providers[0]?.episodesCount || 0)\n          .fill(null)\n          .map((_, i) => i),\n  );\n  const [activeType, setActiveType] = useState(0);\n  const { gotoPage, currentPage, maxPage, currentData } = useSplitArrayIntoPage(\n    episodesAvailable || [],\n    50,\n  );\n\n  const handleProviderChange = async (value: string) => {\n    setSelectedProvider(value);\n    const providerData = providers.find((p) => p.provider === value);\n    if (providerData) {\n      setEpisodesCountProvider(providerData.episodesCount || 0);\n      if (episodesCountAvailable && episodesCountAvailable >= (providerData?.episodesCount || 0)) {\n        setEpisodesAvailable(episodes?.slice(0, providerData?.episodesCount || 0));\n      } else {\n        setActiveType(0);\n        setEpisodesAvailable(\n          Array(providerData?.episodesCount || 0)\n            .fill(null)\n            .map((_, i) => i),\n        );\n      }\n    }\n  };\n\n  const handleSelectEpisode = (index: number) => {\n    const providerData = providers.find((p) => p.provider === selectedProvider);\n    if (type === 'tv')\n      navigate(\n        `/tv-shows/${id}/season/${season}/episode/${\n          index + 1\n        }/watch?provider=${selectedProvider}&id=${providerData?.id}`,\n      );\n    else if (type === 'anime') {\n      navigate(\n        `/anime/${id}/episode/${index + 1}/watch?provider=${selectedProvider}&id=${\n          providerData?.id\n        }&skipOpEd=${isShowSkipOpEdButton.value}`,\n      );\n    }\n  };\n\n  return (\n    <>\n      <div className=\"my-5 flex w-full flex-row flex-wrap items-center justify-between gap-4\">\n        <h3>{t('episodes')}</h3>\n        <div className=\"flex flex-row items-center justify-end gap-2\">\n          {providers ? (\n            <Select value={selectedProvider} onValueChange={(value) => handleProviderChange(value)}>\n              <SelectTrigger aria-label=\"provider\">\n                <SelectValue />\n              </SelectTrigger>\n              <SelectContent>\n                {providers.map((provider) => (\n                  <SelectItem key={provider.provider} value={provider.provider}>\n                    {provider.provider}\n                  </SelectItem>\n                ))}\n              </SelectContent>\n            </Select>\n          ) : null}\n          {episodesCountAvailable && episodesCountAvailable >= episodesCountProvider ? (\n            <ButtonGroup>\n              {episodeTypes.map((episodeType) => (\n                <Button\n                  key={`button-item-${episodeType.activeType}`}\n                  type=\"button\"\n                  onPress={() => setActiveType(episodeType.activeType)}\n                  {...(activeType === episodeType.activeType ? {} : { variant: 'ghost' })}\n                  isIconOnly\n                >\n                  {episodeType.activeTypeName === 'Image' ? (\n                    <PhotoIcon />\n                  ) : (\n                    <ViewGrid width={36} height={36} />\n                  )}\n                </Button>\n              ))}\n            </ButtonGroup>\n          ) : null}\n        </div>\n      </div>\n      {currentData && currentData.length > 0 && (\n        <div\n          className={`flex w-full ${\n            activeType === 0 ? 'flex-row flex-wrap items-center justify-start' : 'flex-col'\n          }`}\n        >\n          {currentData.map((episode, index) =>\n            activeType === 0 ? (\n              <Button\n                key={episode.id}\n                type=\"button\"\n                onPress={() => handleSelectEpisode(index)}\n                className=\"mb-2 mr-2 w-10 p-0\"\n                isIconOnly\n              >\n                {index + 1 + (currentPage - 1) * 50}\n              </Button>\n            ) : activeType === 1 &&\n              episodesCountAvailable &&\n              episodesCountAvailable >= episodesCountProvider ? (\n              <div key={episode.id}>\n                <Card\n                  isHoverable\n                  isPressable\n                  className=\"!max-h-[127px] w-full data-[hover=true]:ring-2 data-[hover=true]:ring-focus\"\n                  onPress={() => handleSelectEpisode(index)}\n                >\n                  <CardBody className=\"flex flex-row flex-nowrap justify-start p-0\">\n                    {type === 'tv' &&\n                      (episode?.still_path ? (\n                        <Image\n                          src={TMDB.posterUrl(episode?.still_path, 'w342')}\n                          width={227}\n                          height=\"100%\"\n                          isZoomed\n                          radius=\"lg\"\n                          loading=\"lazy\"\n                          disableSkeleton={false}\n                          alt={episode?.name || ''}\n                          title={episode?.name || ''}\n                          classNames={{\n                            wrapper: 'm-0 min-w-[227px] max-h-[127px] overflow-hidden',\n                          }}\n                          placeholder=\"empty\"\n                          options={{ contentType: MimeType.WEBP }}\n                          responsive={[\n                            {\n                              size: {\n                                width: 227,\n                                height: 127,\n                              },\n                            },\n                          ]}\n                        />\n                      ) : (\n                        <div className=\"z-0 flex aspect-[16/9] min-h-[125px] min-w-[227px] items-center justify-center rounded-large bg-default\">\n                          <PhotoIcon width={36} height={36} />\n                        </div>\n                      ))}\n                    {type === 'anime' &&\n                      (episode?.image ? (\n                        <Image\n                          src={episode.image}\n                          width={227}\n                          height=\"100%\"\n                          alt={episode?.title || ''}\n                          title={episode?.title || ''}\n                          loading=\"lazy\"\n                          radius=\"lg\"\n                          isZoomed\n                          disableSkeleton={false}\n                          classNames={{\n                            wrapper: 'm-0 min-w-[227px] max-h-[125px] overflow-hidden',\n                          }}\n                          placeholder=\"empty\"\n                          options={{ contentType: MimeType.WEBP }}\n                          responsive={[\n                            {\n                              size: {\n                                width: 227,\n                                height: 127,\n                              },\n                            },\n                          ]}\n                        />\n                      ) : (\n                        <div className=\"z-0 flex aspect-[16/9] min-h-[125px] min-w-[227px] items-center justify-center rounded-large bg-default\">\n                          <PhotoIcon width={48} height={48} />\n                        </div>\n                      ))}\n                    <div className=\"flex flex-col justify-start gap-y-1 p-4\">\n                      <h5 className=\"line-clamp-1\">\n                        {t('episode')}{' '}\n                        {type === 'tv'\n                          ? isSm\n                            ? `${episode?.episode_number}`\n                            : `${episode?.episode_number}: ${episode?.name}`\n                          : null}\n                        {type === 'anime'\n                          ? isSm\n                            ? `${episode?.number}`\n                            : `${episode?.number}: ${episode?.title}`\n                          : null}\n                      </h5>\n                      {type === 'tv' && !isSm && (\n                        <>\n                          <div className=\"flex items-center gap-x-4\">\n                            <Rating rating={episode?.vote_average.toFixed(1)} ratingType=\"tv\" />\n                            <h5>\n                              {episode.air_date} | {episode?.runtime} {t('min')}\n                            </h5>\n                          </div>\n                          <h6 className=\"!line-clamp-1\">{episode.overview}</h6>\n                        </>\n                      )}\n                      {type === 'anime' && !isSm && episode.description && (\n                        <h6 className=\"!line-clamp-2\">{episode.description}</h6>\n                      )}\n                    </div>\n                  </CardBody>\n                </Card>\n                <Spacer y={5} />\n              </div>\n            ) : null,\n          )}\n        </div>\n      )}\n      <Spacer y={6} />\n      {maxPage > 1 ? (\n        <div className=\"flex flex-row justify-center\">\n          <Pagination\n            // showControls={!isSm}\n            total={maxPage}\n            initialPage={currentPage}\n            // shadow\n            onChange={(page) => gotoPage(page)}\n            {...(isSm && { size: 'sm' })}\n          />\n        </div>\n      ) : null}\n    </>\n  );\n};\n\nexport default ListEpisodes;\n"
  },
  {
    "path": "app/components/elements/shared/ListViewChangeButton.tsx",
    "content": "import { Button, ButtonGroup } from '@nextui-org/button';\n\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport ViewGridCard from '~/assets/icons/ViewGridCardIcon';\nimport ViewGridDetail from '~/assets/icons/ViewGridDetailIcon';\nimport ViewGridTable from '~/assets/icons/ViewGridTableIcon';\n\nconst ListViewChangeButton = () => {\n  const { listViewType } = useSoraSettings();\n  return (\n    <ButtonGroup>\n      <Button\n        type=\"button\"\n        onPress={() => listViewType.set('card')}\n        isIconOnly\n        {...(listViewType.value === 'card' ? { color: 'primary' } : { variant: 'ghost' })}\n      >\n        <ViewGridCard width={40} height={40} />\n      </Button>\n      <Button\n        type=\"button\"\n        onPress={() => listViewType.set('detail')}\n        isIconOnly\n        {...(listViewType.value === 'detail' ? { color: 'primary' } : { variant: 'ghost' })}\n      >\n        <ViewGridDetail width={40} height={40} />\n      </Button>\n      <Button\n        type=\"button\"\n        onPress={() => listViewType.set('table')}\n        isIconOnly\n        {...(listViewType.value === 'table' ? { color: 'primary' } : { variant: 'ghost' })}\n      >\n        <ViewGridTable width={40} height={40} />\n      </Button>\n    </ButtonGroup>\n  );\n};\n\nexport default ListViewChangeButton;\n"
  },
  {
    "path": "app/components/elements/shared/Rating.tsx",
    "content": "import { cnBase } from 'tailwind-variants';\n\nimport AnilistStatIcon from '~/assets/icons/AnilistStatIcon';\nimport StarIcon from '~/assets/icons/StarIcon';\n\ninterface IRatingProps {\n  rating: number | string | undefined;\n  ratingType?: 'movie' | 'tv' | 'anime' | 'people';\n  color?: string;\n  className?: string;\n  showStarIcon?: boolean;\n}\n\nconst Rating = (props: IRatingProps) => {\n  const { rating, ratingType, color, className, showStarIcon } = props;\n  if (ratingType === 'movie' || ratingType === 'tv') {\n    return (\n      <div className={cnBase('flex flex-row items-center gap-x-2', className)}>\n        {showStarIcon ? (\n          <StarIcon fill=\"#eda51b\" filled width={16} height={16} />\n        ) : (\n          <p className=\"rounded-large bg-[#3ec2c2] px-1 text-white\">TMDb</p>\n        )}\n        <p style={color ? { color } : {}}>{rating}</p>\n      </div>\n    );\n  }\n  return (\n    <div className={cnBase('flex flex-row items-center gap-x-2', className)}>\n      {Number(rating) > 75 ? (\n        <AnilistStatIcon stat=\"good\" />\n      ) : Number(rating) > 60 ? (\n        <AnilistStatIcon stat=\"average\" />\n      ) : (\n        <AnilistStatIcon stat=\"bad\" />\n      )}\n      <p style={color ? { color } : {}}>{rating}%</p>\n    </div>\n  );\n};\n\nexport default Rating;\n"
  },
  {
    "path": "app/components/elements/shared/ResizablePanel.tsx",
    "content": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { useMemo } from 'react';\nimport { useMeasure, useWindowSize } from '@react-hookz/web';\nimport { AnimatePresence, motion } from 'framer-motion';\n\nimport {\n  ScrollArea,\n  ScrollBar,\n  ScrollCorner,\n  ScrollViewport,\n} from '~/components/elements/ScrollArea';\n\n/*\n  Replacer function to JSON.stringify that ignores\n  circular references and internal React properties.\n  https://github.com/facebook/react/issues/8669#issuecomment-531515508\n*/\nconst ignoreCircularReferences = () => {\n  const seen = new WeakSet();\n  return (key: any, value: any) => {\n    if (key.startsWith('_')) return; // Don't compare React's internal props.\n    if (typeof value === 'object' && value !== null) {\n      if (seen.has(value)) return;\n      seen.add(value);\n    }\n    return value;\n  };\n};\n\nconst ResizablePanel = ({\n  children,\n  contentWidth,\n}: {\n  children: React.ReactNode;\n  contentWidth: 'full' | 'fit';\n}) => {\n  const [size, ref] = useMeasure<HTMLDivElement>();\n  const screen = useWindowSize();\n  const panelHeight = useMemo(() => {\n    if (size?.height && screen?.height) {\n      if ((size?.height || 0) + 10 > 400) {\n        return screen?.height > 400 ? 386 : screen.height - 36;\n      }\n      return (size?.height || 0) + 10 > screen?.height ? screen.height - 36 : size?.height;\n    }\n    return 'auto';\n  }, [screen?.height, size?.height]);\n\n  return (\n    <motion.div\n      className=\"relative overflow-hidden\"\n      animate={{\n        height: contentWidth === 'fit' ? (panelHeight as number) + 16 : panelHeight,\n        width: size?.width ? (contentWidth === 'fit' ? size?.width + 16 : size?.width) : 'auto',\n      }}\n      transition={{ duration: 0.3 }}\n    >\n      <AnimatePresence initial={false}>\n        <motion.div // slide and fade effect\n          key={JSON.stringify(children, ignoreCircularReferences())}\n          initial={{ x: 382, opacity: 0 }}\n          animate={{ x: 0, opacity: 1 }}\n          exit={{ x: -382, opacity: 0 }}\n          transition={{ duration: 0.3, ease: 'easeInOut' }}\n          className={size?.height ? 'absolute' : 'relative'}\n        >\n          <ScrollArea\n            type=\"hover\"\n            style={{\n              height: contentWidth === 'fit' ? (panelHeight as number) + 16 : panelHeight,\n              width: size?.width\n                ? contentWidth === 'fit'\n                  ? size?.width + 16\n                  : size?.width\n                : 'auto',\n            }}\n          >\n            <ScrollViewport className={contentWidth === 'fit' ? '!p-2' : undefined}>\n              <div\n                ref={ref}\n                className={`${\n                  contentWidth === 'fit' ? 'w-fit' : contentWidth === 'full' ? 'w-full' : ''\n                }`}\n              >\n                {children}\n              </div>\n            </ScrollViewport>\n            <ScrollBar />\n            <ScrollCorner />\n          </ScrollArea>\n        </motion.div>\n      </AnimatePresence>\n    </motion.div>\n  );\n};\n\nexport default ResizablePanel;\n"
  },
  {
    "path": "app/components/elements/shared/WatchDetail.tsx",
    "content": "import { memo } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { Spacer } from '@nextui-org/spacer';\nimport { useMediaQuery } from '@react-hookz/web';\nimport { useNavigate } from '@remix-run/react';\nimport { MimeType } from 'remix-image';\n\nimport type { IMedia } from '~/types/media';\nimport type { IEpisodeInfo } from '~/services/consumet/anilist/anilist.types';\nimport type { IEpisode } from '~/services/tmdb/tmdb.types';\nimport MediaList from '~/components/media/MediaList';\nimport ListEpisodes from '~/components/elements/shared/ListEpisodes';\nimport Rating from '~/components/elements/shared/Rating';\nimport PhotoIcon from '~/assets/icons/PhotoIcon';\n\nimport Image from '../Image';\n\ninterface IWatchDetailProps {\n  id?: number | string | undefined;\n  type: 'movie' | 'tv' | 'anime';\n  title: string;\n  overview?: string;\n  posterPath?: string;\n  tmdbRating?: number;\n  imdbRating?: number;\n  anilistRating?: number;\n  genresMedia?: {\n    id?: number;\n    name?: string;\n  }[];\n  genresAnime?: string[];\n  recommendationsMovies?: IMedia[];\n  recommendationsAnime?: IMedia[];\n  genresMovie?: { [id: string]: string };\n  genresTv?: { [id: string]: string };\n  color?: string;\n  episodes?: IEpisode[] | IEpisodeInfo[];\n  season?: number;\n  providers?: {\n    id?: string | number | null;\n    provider: string;\n    episodesCount?: number;\n  }[];\n}\n\nconst WatchDetail: React.FC<IWatchDetailProps> = (props: IWatchDetailProps) => {\n  const {\n    id,\n    type,\n    title,\n    overview,\n    posterPath,\n    tmdbRating,\n    imdbRating,\n    anilistRating,\n    genresMedia,\n    genresAnime,\n    recommendationsMovies,\n    recommendationsAnime,\n    genresMovie,\n    genresTv,\n    episodes,\n    season,\n    providers,\n  } = props;\n  const navigate = useNavigate();\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const isMd = useMediaQuery('(max-width: 768px)', { initializeWithValue: false });\n\n  return (\n    <>\n      {type === 'anime' || type === 'tv' ? (\n        <>\n          {type === 'anime' ? (\n            <ListEpisodes\n              type=\"anime\"\n              id={id}\n              episodes={episodes}\n              providers={providers || []}\n              // episodeId\n              // episodeNumber\n            />\n          ) : null}\n          {type === 'tv' ? (\n            <ListEpisodes\n              type=\"tv\"\n              id={id}\n              episodes={episodes}\n              season={season}\n              providers={providers || []}\n            />\n          ) : null}\n          <Spacer y={10} />\n        </>\n      ) : null}\n      <div className=\"flex w-full flex-row items-start justify-start gap-3 rounded-large bg-content1 p-2 sm:p-4\">\n        {!isMd &&\n          (posterPath ? (\n            <Image\n              src={posterPath}\n              alt={title}\n              title={title}\n              disableSkeleton={false}\n              radius=\"md\"\n              classNames={{\n                wrapper:\n                  'w-full h-auto aspect-[2/3] min-w-[auto] min-h-[auto] !max-w-[137px] lg:!max-w-[158px] xl:!max-w-[173px] 2xl:!max-w-[239px]',\n              }}\n              loading=\"lazy\"\n              placeholder=\"empty\"\n              responsive={[\n                {\n                  size: {\n                    width: 137,\n                    height: 205,\n                  },\n                  maxWidth: 1024,\n                },\n                {\n                  size: {\n                    width: 158,\n                    height: 237,\n                  },\n                  maxWidth: 1280,\n                },\n                {\n                  size: {\n                    width: 173,\n                    height: 260,\n                  },\n                  maxWidth: 1400,\n                },\n                {\n                  size: {\n                    width: 239,\n                    height: 359,\n                  },\n                },\n              ]}\n              options={{\n                contentType: MimeType.WEBP,\n              }}\n            />\n          ) : (\n            <div className=\"z-0 flex aspect-[2/3] h-auto w-[137px] items-center justify-center rounded-medium bg-default lg:w-[158px] xl:w-[173px] 2xl:w-[239px]\">\n              <PhotoIcon width={36} height={36} />\n            </div>\n          ))}\n        <div className=\"flex w-full flex-col items-start justify-start gap-y-4\">\n          <h3 className=\"font-semibold text-default-900\">{title}</h3>\n          {type === 'movie' || type === 'tv' ? (\n            <div className=\"flex flex-row gap-x-6\">\n              <Rating rating={tmdbRating?.toFixed(1)} ratingType=\"movie\" />\n              {imdbRating && (\n                <div className=\"flex flex-row items-center gap-x-2\">\n                  <p className=\"rounded-large bg-[#ddb600] px-1 text-black\">IMDb</p>\n                  <p>{imdbRating}</p>\n                </div>\n              )}\n            </div>\n          ) : null}\n          {type === 'anime' && anilistRating ? (\n            <Rating rating={anilistRating} ratingType=\"anime\" />\n          ) : null}\n          <div className=\"flex w-full flex-row flex-wrap items-center justify-start gap-x-3\">\n            {(type === 'movie' || type === 'tv') && genresMedia\n              ? genresMedia.map((genre) => (\n                  <Button\n                    key={genre?.id}\n                    type=\"button\"\n                    size={isSm ? 'sm' : 'md'}\n                    className=\"mb-1 hover:opacity-80\"\n                    onPress={() =>\n                      navigate(\n                        `/discover/${type === 'movie' ? 'movies' : 'tv-shows'}?with_genres=${\n                          genre?.id\n                        }`,\n                      )\n                    }\n                  >\n                    {genre?.name}\n                  </Button>\n                ))\n              : null}\n            {type === 'anime'\n              ? genresAnime &&\n                genresAnime.map((genre, index) => (\n                  <Button\n                    key={index}\n                    type=\"button\"\n                    size={isSm ? 'sm' : 'md'}\n                    className=\"mb-1 hover:opacity-80\"\n                    onPress={() => navigate(`/discover/anime?genres=${genre}`)}\n                  >\n                    {genre}\n                  </Button>\n                ))\n              : null}\n          </div>\n          {type === 'movie' || type === 'tv' ? (\n            <p style={{ textAlign: 'justify' }}>{overview}</p>\n          ) : null}\n          {type === 'anime' ? (\n            <p\n              style={{ textAlign: 'justify' }}\n              dangerouslySetInnerHTML={{ __html: overview || '' }}\n            />\n          ) : null}\n        </div>\n      </div>\n      <Spacer y={10} />\n      {(type === 'movie' || type === 'tv') &&\n      recommendationsMovies &&\n      recommendationsMovies.length > 0 ? (\n        <>\n          <MediaList\n            genresMovie={genresMovie}\n            genresTv={genresTv}\n            items={recommendationsMovies}\n            itemsType={type}\n            listName=\"You May Also Like\"\n            listType=\"slider-card\"\n            navigationButtons\n            onClickViewMore={() =>\n              navigate(`/${type === 'movie' ? 'movies' : 'tv-shows'}/${id}/recommendations`)\n            }\n            showMoreList\n          />\n          <Spacer y={10} />\n        </>\n      ) : null}\n      {type === 'anime' && recommendationsAnime && recommendationsAnime.length > 0 ? (\n        <>\n          <MediaList\n            items={recommendationsAnime}\n            itemsType=\"anime\"\n            listName=\"You May Also Like\"\n            listType=\"slider-card\"\n            navigationButtons\n          />\n          <Spacer y={10} />\n        </>\n      ) : null}\n    </>\n  );\n};\n\nexport default memo(WatchDetail);\n"
  },
  {
    "path": "app/components/elements/tab/TabLink.tsx",
    "content": "import { useRef } from 'react';\nimport { useDebouncedEffect, useMediaQuery } from '@react-hookz/web';\nimport { NavLink, useLocation } from '@remix-run/react';\nimport { motion } from 'framer-motion';\nimport { useTranslation } from 'react-i18next';\n\nimport {\n  ScrollArea,\n  ScrollBar,\n  ScrollCorner,\n  ScrollViewport,\n} from '~/components/elements/ScrollArea';\n\ninterface ITabProps {\n  pages?: {\n    pageName: string;\n    pageLink: string;\n  }[];\n  linkTo?: string;\n}\n\nconst TabLink = (props: ITabProps) => {\n  const { pages, linkTo } = props;\n  const { t } = useTranslation();\n  const location = useLocation();\n  const underlineRef = useRef<HTMLDivElement>(null);\n  useDebouncedEffect(\n    // need to debounce this because the scrollIntoView is called before the underline is rendered\n    () => {\n      if (underlineRef.current) {\n        underlineRef.current.scrollIntoView({\n          behavior: 'smooth',\n          block: 'center',\n          inline: 'center',\n        });\n      }\n    },\n    [location],\n    350,\n  );\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  return (\n    <ScrollArea\n      type={isSm ? 'scroll' : 'hover'}\n      scrollHideDelay={100}\n      className=\"z-[2] w-full border-b border-divider\"\n    >\n      <ScrollViewport>\n        <div className=\"flex focus:outline-none\">\n          {pages?.map((page) => (\n            <NavLink\n              key={page.pageLink}\n              to={`${linkTo}${page.pageLink}`}\n              className=\"relative z-10 flex h-12 shrink-0 items-center justify-center rounded-large p-4 outline-none hover:opacity-80 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-focus\"\n            >\n              {({ isActive }) => (\n                <>\n                  <h6 className=\"font-semibold\">{t(page.pageName)}</h6>\n                  {isActive ? (\n                    <motion.div\n                      ref={underlineRef}\n                      layoutId=\"underline\"\n                      className=\"absolute bottom-0 h-1 w-1/2 overflow-hidden rounded-small bg-default-foreground\"\n                    />\n                  ) : null}\n                </>\n              )}\n            </NavLink>\n          ))}\n        </div>\n      </ScrollViewport>\n      <ScrollBar orientation=\"horizontal\" className=\"opacity-30\" />\n      <ScrollCorner />\n    </ScrollArea>\n  );\n};\n\nexport default TabLink;\n"
  },
  {
    "path": "app/components/elements/tab/Tabs.tsx",
    "content": "import * as React from 'react';\nimport * as TabsPrimitive from '@radix-ui/react-tabs';\nimport { cnBase } from 'tailwind-variants';\n\nimport {\n  ScrollArea,\n  ScrollBar,\n  ScrollCorner,\n  ScrollViewport,\n} from '~/components/elements/ScrollArea';\n\nconst Tabs = React.forwardRef<\n  React.ElementRef<typeof TabsPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof TabsPrimitive.Root>\n>(({ className, ...props }, ref) => (\n  <TabsPrimitive.Root\n    ref={ref}\n    className={cnBase('flex data-[orientation=horizontal]:flex-col', className)}\n    {...props}\n  />\n));\nTabs.displayName = TabsPrimitive.Root.displayName;\n\nconst TabsList = React.forwardRef<\n  React.ElementRef<typeof TabsPrimitive.List>,\n  React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>\n>(({ className, ...props }, ref) => (\n  <ScrollArea\n    type=\"scroll\"\n    scrollHideDelay={100}\n    className=\"h-[61px] w-full rounded-bl-none sm:h-auto sm:w-[200px] sm:min-w-[200px] sm:rounded-l-small sm:rounded-r-none\"\n  >\n    <ScrollViewport>\n      <TabsPrimitive.List\n        ref={ref}\n        className={cnBase(\n          'inline-flex w-full shrink-0 gap-y-3 rounded-medium bg-content1 p-[5px] focus:outline-none data-[orientation=vertical]:flex-col',\n          className,\n        )}\n        {...props}\n      />\n    </ScrollViewport>\n    <ScrollBar />\n    <ScrollCorner />\n  </ScrollArea>\n));\nTabsList.displayName = TabsPrimitive.List.displayName;\n\nconst TabsTrigger = React.forwardRef<\n  React.ElementRef<typeof TabsPrimitive.Trigger>,\n  React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>\n>(({ className, ...props }, ref) => (\n  <TabsPrimitive.Trigger\n    ref={ref}\n    className={cnBase(\n      'z-10 inline-flex h-[50px] shrink-0 select-none items-center justify-center rounded-small p-3 text-default-500 outline-none hover:opacity-70 data-[disabled]:cursor-not-allowed data-[orientation=vertical]:justify-start data-[state=active]:bg-default data-[disabled]:text-gray-500 data-[state=active]:text-default-foreground data-[disabled]:opacity-70',\n      className,\n    )}\n    {...props}\n  />\n));\nTabsTrigger.displayName = TabsPrimitive.Trigger.displayName;\n\nconst TabsContent = React.forwardRef<\n  React.ElementRef<typeof TabsPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>\n>(({ className, ...props }, ref) => (\n  <TabsPrimitive.Content\n    ref={ref}\n    className={cnBase(\n      'min-h-[75vh] grow rounded-small outline-none sm:rounded-bl-none sm:rounded-tr-medium sm:px-[10px] xl:px-5',\n      className,\n    )}\n    {...props}\n  />\n));\nTabsContent.displayName = TabsPrimitive.Content.displayName;\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent };\n"
  },
  {
    "path": "app/components/layouts/ActionButtons.tsx",
    "content": "import { Button } from '@nextui-org/button';\nimport { motion, useTransform } from 'framer-motion';\n\nimport { useLayout } from '~/store/layout/useLayout';\nimport Arrow from '~/assets/icons/ArrowIcon';\n\nconst ActionButtons = () => {\n  const { scrollY, viewportRef } = useLayout((state) => state);\n  const opacity = useTransform(scrollY, [0, 80], [0, 1]);\n  const y = useTransform(scrollY, [0, 80], [100, 0]);\n  const handleButtonPress = () => {\n    viewportRef.current?.scrollTo({ top: 0, behavior: 'smooth' });\n  };\n  return (\n    <motion.div\n      style={{ opacity, y }}\n      className=\"fixed bottom-24 right-8 z-[3999] flex flex-col items-center sm:bottom-8\"\n    >\n      <Button\n        color=\"primary\"\n        size=\"lg\"\n        radius=\"full\"\n        isIconOnly\n        onPress={() => handleButtonPress()}\n        className=\"shadow-2xl\"\n      >\n        <Arrow direction=\"up\" />\n      </Button>\n    </motion.div>\n  );\n};\n\nexport default ActionButtons;\n"
  },
  {
    "path": "app/components/layouts/BottomNav.tsx",
    "content": "/* eslint-disable @typescript-eslint/indent */\nimport { useState } from 'react';\nimport { NavLink, useLocation, useSearchParams } from '@remix-run/react';\nimport type { User } from '@supabase/supabase-js';\nimport { motion } from 'framer-motion';\nimport { useTranslation } from 'react-i18next';\nimport { tv } from 'tailwind-variants';\n\nimport { useLayout } from '~/store/layout/useLayout';\nimport { Sheet, SheetContent, SheetTrigger } from '~/components/elements/Sheet';\nimport Category from '~/assets/icons/CategoryIcon';\nimport Discover from '~/assets/icons/DiscoverIcon';\nimport History from '~/assets/icons/HistoryIcon';\nimport Home from '~/assets/icons/HomeIcon';\nimport LogIn from '~/assets/icons/LogInIcon';\nimport LogOut from '~/assets/icons/LogOutIcon';\nimport Menu from '~/assets/icons/MenuIcon';\nimport Settings from '~/assets/icons/SettingsIcon';\n\ninterface IBottomNavProps {\n  user?: User | undefined;\n}\n\nconst BottomNav = (props: IBottomNavProps) => {\n  const { user } = props;\n  const { t } = useTranslation();\n  const [openMore, setOpenMore] = useState(false);\n  const location = useLocation();\n  const [search] = useSearchParams();\n  const scrollDirection = useLayout((state) => state.scrollDirection);\n  const bottomNavItemStyles = tv({\n    base: 'flex flex-col items-center justify-center gap-y-2 rounded-small bg-transparent text-xs font-medium text-foreground',\n    variants: {\n      active: {\n        true: 'text-primary',\n        false: '',\n      },\n    },\n  });\n  const ref = (search.get('ref') || location.pathname + location.search)\n    .replace('?', '_0x3F_')\n    .replace('&', '_0x26');\n\n  const moreNavItems = [\n    {\n      name: 'lists',\n      icon: Category,\n      path: '/lists',\n    },\n    {\n      name: 'watch-history',\n      icon: History,\n      path: '/watch-history',\n    },\n    ...(user\n      ? [\n          {\n            name: 'logout',\n            icon: LogOut,\n            path: `/sign-out?ref=${ref}`,\n          },\n        ]\n      : [\n          {\n            name: 'login',\n            icon: LogIn,\n            path: `/sign-in?ref=${ref}`,\n          },\n        ]),\n    {\n      name: 'settings',\n      icon: Settings,\n      path: '/settings',\n    },\n  ];\n\n  return (\n    <motion.div\n      initial={{ y: 0 }}\n      animate={{ y: scrollDirection === 'down' ? 65 : 0 }}\n      transition={{ duration: 0.5 }}\n      className=\"fixed bottom-0 z-[4000] flex h-16 w-full flex-row flex-nowrap items-center justify-around border-t border-divider bg-background/[0.6] py-2 drop-shadow-md backdrop-blur-2xl backdrop-contrast-125 backdrop-saturate-200 sm:hidden\"\n    >\n      <NavLink\n        to=\"/\"\n        className={({ isActive }) =>\n          bottomNavItemStyles({\n            active: isActive,\n          })\n        }\n      >\n        {({ isActive }) => (\n          <>\n            <Home filled={isActive} />\n            {t('home')}\n          </>\n        )}\n      </NavLink>\n      <NavLink\n        to=\"/discover\"\n        className={({ isActive }) =>\n          bottomNavItemStyles({\n            active: isActive,\n          })\n        }\n      >\n        {({ isActive }) => (\n          <>\n            <Discover filled={isActive} />\n            {t('discover')}\n          </>\n        )}\n      </NavLink>\n      <Sheet open={openMore} onOpenChange={(open) => setOpenMore(open)}>\n        <SheetTrigger asChild>\n          <button type=\"button\" className={bottomNavItemStyles()}>\n            <Menu />\n            {t('more')}\n          </button>\n        </SheetTrigger>\n        <SheetContent\n          side=\"bottom\"\n          size=\"content\"\n          hideCloseButton\n          swipeDownToClose\n          open={openMore}\n          onOpenChange={() => setOpenMore(!openMore)}\n        >\n          <div className=\"my-4 grid grid-cols-3 items-center justify-center gap-x-3 gap-y-5 p-2 xs:grid-cols-4\">\n            {moreNavItems.map((item) => (\n              <NavLink\n                key={item.name}\n                to={item.path}\n                className={({ isActive }) =>\n                  bottomNavItemStyles({\n                    active: isActive,\n                  })\n                }\n                onClick={() => setOpenMore(false)}\n              >\n                {({ isActive }) => (\n                  <>\n                    <item.icon filled={isActive} />\n                    {t(item.name)}\n                  </>\n                )}\n              </NavLink>\n            ))}\n          </div>\n        </SheetContent>\n      </Sheet>\n    </motion.div>\n  );\n};\n\nexport default BottomNav;\n"
  },
  {
    "path": "app/components/layouts/ControlNavigation.tsx",
    "content": "import { Button } from '@nextui-org/button';\nimport { Chip } from '@nextui-org/chip';\nimport { useMatches, useNavigate, useParams, type UIMatch } from '@remix-run/react';\nimport { motion, useTransform } from 'framer-motion';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { useHeaderOptions } from '~/utils/react/hooks/useHeader';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport { useHeaderStyle } from '~/store/layout/useHeaderStyle';\nimport { useHistoryStack } from '~/store/layout/useHistoryStack';\nimport { useLayout } from '~/store/layout/useLayout';\nimport { Breadcrumb } from '~/components/elements/Breadcrumb';\nimport ChevronLeft from '~/assets/icons/ChevronLeftIcon';\nimport ChevronRight from '~/assets/icons/ChevronRightIcon';\n\nconst ControlNavigation = () => {\n  const navigate = useNavigate();\n  const matches = useMatches() as UIMatch<unknown, Handle>[];\n  const isHydrated = useHydrated();\n  const params = useParams();\n  const { t } = useTranslation();\n  const { isShowBreadcrumb } = useSoraSettings();\n  const { scrollY } = useLayout((state) => state);\n  const { startChangeScrollPosition } = useHeaderStyle((state) => state);\n  const { currentMiniTitle, customHeaderChangeColorOnScroll } = useHeaderOptions();\n  const { historyBack, historyForward } = useHistoryStack((state) => state);\n  const opacity = useTransform(\n    scrollY,\n    [0, startChangeScrollPosition, startChangeScrollPosition + 80],\n    [0, 0, customHeaderChangeColorOnScroll ? (startChangeScrollPosition ? 1 : 0) : 1],\n  );\n  const y = useTransform(\n    scrollY,\n    [0, startChangeScrollPosition, startChangeScrollPosition + 80],\n    [60, 60, customHeaderChangeColorOnScroll ? (startChangeScrollPosition ? 0 : 60) : 0],\n  );\n  const handleNavigationBackForward = (direction: 'back' | 'forward') => {\n    if (direction === 'back') {\n      navigate(-1);\n    } else if (direction === 'forward') {\n      navigate(1);\n    }\n  };\n\n  return (\n    <div className=\"flex flex-row items-center justify-center gap-x-2\">\n      <Button\n        variant=\"faded\"\n        radius=\"full\"\n        isIconOnly\n        onPress={() => handleNavigationBackForward('back')}\n        isDisabled={historyBack.length <= 1}\n        className=\"h-9 w-9\"\n        size=\"sm\"\n      >\n        <ChevronLeft />\n      </Button>\n      <Button\n        variant=\"faded\"\n        radius=\"full\"\n        isIconOnly\n        onPress={() => handleNavigationBackForward('forward')}\n        isDisabled={historyForward.length <= 1}\n        className=\"h-9 w-9\"\n        size=\"sm\"\n      >\n        <ChevronRight />\n      </Button>\n      {isHydrated ? (\n        isShowBreadcrumb.value ? (\n          <Chip color=\"default\" variant=\"faded\" size=\"lg\">\n            <Breadcrumb>\n              {matches\n                // skip routes that don't have a breadcrumb\n                .filter((match) => match.handle && match.handle.breadcrumb)\n                // render breadcrumbs!\n                .map((match) => match.handle?.breadcrumb?.({ match, t, params }))}\n            </Breadcrumb>\n          </Chip>\n        ) : currentMiniTitle ? (\n          <motion.div\n            style={{ opacity, y }}\n            transition={{ duration: 0.3 }}\n            className=\"flex flex-row items-center justify-start gap-x-3\"\n          >\n            {currentMiniTitle.showImage ? (\n              <img\n                src={currentMiniTitle.imageUrl}\n                alt={`${currentMiniTitle.title} mini`}\n                width={36}\n                height={54}\n                loading=\"lazy\"\n                className=\"rounded-small\"\n              />\n            ) : null}\n            <div className=\"flex flex-col items-start justify-center\">\n              <span className=\"text-2xl font-bold\">{currentMiniTitle.title}</span>\n              {currentMiniTitle.subtitle ? (\n                <span className=\"text-sm font-medium opacity-75\">{currentMiniTitle.subtitle}</span>\n              ) : null}\n            </div>\n          </motion.div>\n        ) : null\n      ) : null}\n    </div>\n  );\n};\n\nexport default ControlNavigation;\n"
  },
  {
    "path": "app/components/layouts/GlobalPlayer.tsx",
    "content": "/* eslint-disable @typescript-eslint/no-non-null-assertion */\n/* eslint-disable react-hooks/exhaustive-deps */\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { Tooltip } from '@nextui-org/tooltip';\nimport { useMeasure } from '@react-hookz/web';\nimport {\n  useFetcher,\n  useLocation,\n  useMatches,\n  useNavigate,\n  useParams,\n  useRouteLoaderData,\n  type UIMatch,\n} from '@remix-run/react';\nimport type Artplayer from 'artplayer';\nimport { AnimatePresence, motion, useMotionValue } from 'framer-motion';\nimport Hls from 'hls.js';\nimport { isDesktop, isMobile, isMobileOnly } from 'react-device-detect';\nimport { createPortal } from 'react-dom';\nimport { toast } from 'sonner';\nimport tinycolor from 'tinycolor2';\n\nimport type { Handle } from '~/types/handle';\nimport type { IVideos } from '~/services/tmdb/tmdb.types';\nimport updateHistory from '~/utils/client/update-history';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport { useLayout } from '~/store/layout/useLayout';\nimport usePlayerState, { type PlayerData } from '~/store/player/usePlayerState';\nimport { Dialog, DialogContent, DialogTrigger } from '~/components/elements/Dialog';\nimport WatchTrailer, { type Trailer } from '~/components/elements/dialog/WatchTrailerDialog';\nimport Player from '~/components/elements/player/ArtPlayer';\nimport PlayerError from '~/components/elements/player/PlayerError';\nimport PlayerHotKey from '~/components/elements/player/PlayerHotkey';\nimport PlayerSettings from '~/components/elements/player/PlayerSettings';\nimport { playerStyles } from '~/components/elements/player/playerStyles';\nimport Expand from '~/assets/icons/ExpandIcon';\nimport Next from '~/assets/icons/NextIcon';\nimport Pause from '~/assets/icons/PauseIcon';\nimport Play from '~/assets/icons/PlayIcon';\nimport Previous from '~/assets/icons/PreviousIcon';\n\ntype Highlight = {\n  start: number;\n  end: number;\n  text: string;\n};\n\nconst GlobalPlayer = () => {\n  const location = useLocation();\n  const navigate = useNavigate();\n  const fetcher = useFetcher();\n  const matches = useMatches() as UIMatch<unknown, Handle>[];\n  const { seasonId, episodeId } = useParams();\n  const {\n    isMini,\n    shouldShowPlayer,\n    setIsMini,\n    setShouldShowPlayer,\n    routePlayer,\n    setRoutePlayer,\n    titlePlayer,\n    setTitlePlayer,\n    playerData,\n    setPlayerData,\n    qualitySelector,\n    setQualitySelector,\n    subtitleSelector,\n    setSubtitleSelector,\n  } = usePlayerState((state) => state);\n  const { isShowOverlay, setIsShowOverlay } = useLayout((state) => state);\n\n  const {\n    provider,\n    sources,\n    subtitles,\n    id,\n    posterPlayer,\n    typeVideo,\n    trailerAnime,\n    hasNextEpisode,\n    idProvider,\n    userId,\n    subtitleOptions,\n    highlights,\n  } = playerData || {};\n  let backgroundColor;\n  let windowColor;\n  let hls: Hls | null = null;\n  const matchesFiltered = useMemo(\n    () =>\n      matches.find(\n        (match) => match?.pathname.includes('player') || match?.pathname.includes('watch'),\n      ),\n    [matches],\n  );\n  const playerSettings = matchesFiltered?.handle?.playerSettings;\n  const shouldPlayInBackground = useMemo(\n    () => !(location?.pathname.includes('player') || location?.pathname.includes('watch')),\n    [location?.pathname],\n  );\n  const {\n    autoShowSubtitle,\n    currentSubtitleFontColor,\n    currentSubtitleFontSize,\n    currentSubtitleBackgroundColor,\n    currentSubtitleBackgroundOpacity,\n    currentSubtitleWindowColor,\n    currentSubtitleWindowOpacity,\n    currentSubtitleTextEffects,\n    isAutoSize,\n    isPicInPic,\n    isMuted,\n    isAutoPlay,\n    isAutoMini,\n    isLoop,\n    isScreenshot,\n    isMiniProgressBar,\n    isAutoPlayback,\n    isAutoPlayNextEpisode,\n    isAutoSkipOpEd,\n    isFastForward,\n    isShowSkipOpEdButton,\n  } = useSoraSettings();\n  const currentEpisode = useMemo(() => Number(episodeId), [episodeId]);\n  const [size, ref] = useMeasure<HTMLDivElement>();\n  const constraintsRef = useRef<HTMLDivElement>(null);\n  const [artplayer, setArtplayer] = useState<Artplayer | null>(null);\n  const [isPlayerPlaying, setIsPlayerPlaying] = useState(false);\n  const [isVideoEnded, setIsVideoEnded] = useState(false);\n  const [isPlayerFullScreen, setIsPlayerFullScreen] = useState(false);\n  const [isSettingsOpen, setSettingsOpen] = useState(false);\n  const [showSubtitle, setShowSubtitle] = useState(autoShowSubtitle.value!);\n  const [showSkipButton, setShowSkipButton] = useState(false);\n  const [currentHighlight, setCurrentHighlight] = useState<Highlight | null>(null);\n  const subtitleColor = useMemo(() => {\n    switch (currentSubtitleFontColor.value) {\n      case 'White':\n        return '#fff';\n      case 'Blue':\n        return '#0072F5';\n      case 'Purple':\n        return '#7828C8';\n      case 'Green':\n        return '#17C964';\n      case 'Yellow':\n        return '#F5A524';\n      case 'Red':\n        return '#F31260';\n      case 'Cyan':\n        return '#06B7DB';\n      case 'Pink':\n        return '#FF4ECD';\n      case 'Black':\n        return '#000';\n      default:\n        return '#fff';\n    }\n  }, [currentSubtitleFontColor.value]);\n  const subtitleBackgroundColor = useMemo(() => {\n    switch (currentSubtitleBackgroundColor.value) {\n      case 'Black':\n        backgroundColor = tinycolor('#000000');\n        backgroundColor.setAlpha(\n          Number(currentSubtitleBackgroundOpacity.value?.replace(/%/g, '')) / 100,\n        );\n        return backgroundColor.toHslString();\n      case 'Blue':\n        backgroundColor = tinycolor('#0072F5');\n        backgroundColor.setAlpha(\n          Number(currentSubtitleBackgroundOpacity.value?.replace(/%/g, '')) / 100,\n        );\n        return backgroundColor.toHslString();\n      case 'Purple':\n        backgroundColor = tinycolor('#7828C8');\n        backgroundColor.setAlpha(\n          Number(currentSubtitleBackgroundOpacity.value?.replace(/%/g, '')) / 100,\n        );\n        return backgroundColor.toHslString();\n      case 'Green':\n        backgroundColor = tinycolor('#17C964');\n        backgroundColor.setAlpha(\n          Number(currentSubtitleBackgroundOpacity.value?.replace(/%/g, '')) / 100,\n        );\n        return backgroundColor.toHslString();\n      case 'Yellow':\n        backgroundColor = tinycolor('#F5A524');\n        backgroundColor.setAlpha(\n          Number(currentSubtitleBackgroundOpacity.value?.replace(/%/g, '')) / 100,\n        );\n        return backgroundColor.toHslString();\n      case 'Red':\n        backgroundColor = tinycolor('#F31260');\n        backgroundColor.setAlpha(\n          Number(currentSubtitleBackgroundOpacity.value?.replace(/%/g, '')) / 100,\n        );\n        return backgroundColor.toHslString();\n      case 'Cyan':\n        backgroundColor = tinycolor('#06B7DB');\n        backgroundColor.setAlpha(\n          Number(currentSubtitleBackgroundOpacity.value?.replace(/%/g, '')) / 100,\n        );\n        return backgroundColor.toHslString();\n      case 'Pink':\n        backgroundColor = tinycolor('#FF4ECD');\n        backgroundColor.setAlpha(\n          Number(currentSubtitleBackgroundOpacity.value?.replace(/%/g, '')) / 100,\n        );\n        return backgroundColor.toHslString();\n      case 'White':\n        backgroundColor = tinycolor('#FFFFFF');\n        backgroundColor.setAlpha(\n          Number(currentSubtitleBackgroundOpacity.value?.replace(/%/g, '')) / 100,\n        );\n        return backgroundColor.toHslString();\n      default:\n        break;\n    }\n  }, [currentSubtitleBackgroundColor.value, currentSubtitleBackgroundOpacity.value]);\n  const subtitleWindowColor = useMemo(() => {\n    switch (currentSubtitleWindowColor.value) {\n      case 'Black':\n        windowColor = tinycolor('#000000');\n        windowColor.setAlpha(Number(currentSubtitleWindowOpacity.value?.replace(/%/g, '')) / 100);\n        return windowColor.toHslString();\n      case 'Blue':\n        windowColor = tinycolor('#0072F5');\n        windowColor.setAlpha(Number(currentSubtitleWindowOpacity.value?.replace(/%/g, '')) / 100);\n        return windowColor.toHslString();\n      case 'Purple':\n        windowColor = tinycolor('#7828C8');\n        windowColor.setAlpha(Number(currentSubtitleWindowOpacity.value?.replace(/%/g, '')) / 100);\n        return windowColor.toHslString();\n      case 'Green':\n        windowColor = tinycolor('#17C964');\n        windowColor.setAlpha(Number(currentSubtitleWindowOpacity.value?.replace(/%/g, '')) / 100);\n        return windowColor.toHslString();\n      case 'Yellow':\n        windowColor = tinycolor('#F5A524');\n        windowColor.setAlpha(Number(currentSubtitleWindowOpacity.value?.replace(/%/g, '')) / 100);\n        return windowColor.toHslString();\n      case 'Red':\n        windowColor = tinycolor('#F31260');\n        windowColor.setAlpha(Number(currentSubtitleWindowOpacity.value?.replace(/%/g, '')) / 100);\n        return windowColor.toHslString();\n      case 'Cyan':\n        windowColor = tinycolor('#06B7DB');\n        windowColor.setAlpha(Number(currentSubtitleWindowOpacity.value?.replace(/%/g, '')) / 100);\n        return windowColor.toHslString();\n      case 'Pink':\n        windowColor = tinycolor('#FF4ECD');\n        windowColor.setAlpha(Number(currentSubtitleWindowOpacity.value?.replace(/%/g, '')) / 100);\n        return windowColor.toHslString();\n      case 'White':\n        windowColor = tinycolor('#FFFFFF');\n        windowColor.setAlpha(Number(currentSubtitleWindowOpacity.value?.replace(/%/g, '')) / 100);\n        return windowColor.toHslString();\n      default:\n        break;\n    }\n  }, [currentSubtitleWindowColor.value, currentSubtitleWindowOpacity.value]);\n  const subtitleFontSize = useMemo(\n    () =>\n      currentSubtitleFontSize.value === '50%'\n        ? `${(size?.height || 0) * 0.05 * 0.5}px`\n        : currentSubtitleFontSize.value === '75%'\n        ? `${(size?.height || 0) * 0.05 * 0.75}px`\n        : currentSubtitleFontSize.value === '100%'\n        ? `${(size?.height || 0) * 0.05}px`\n        : currentSubtitleFontSize.value === '125%'\n        ? `${(size?.height || 0) * 0.05 * 1.25}px`\n        : currentSubtitleFontSize.value === '150%'\n        ? `${(size?.height || 0) * 0.05 * 1.5}px`\n        : currentSubtitleFontSize.value === '175%'\n        ? `${(size?.height || 0) * 0.05 * 1.75}px`\n        : currentSubtitleFontSize.value === '200%'\n        ? `${(size?.height || 0) * 0.05 * 2}px`\n        : currentSubtitleFontSize.value === '300%'\n        ? `${(size?.height || 0) * 0.05 * 3}px`\n        : `${(size?.height || 0) * 0.05 * 4}px`,\n    [currentSubtitleFontSize.value, size?.height],\n  );\n  const subtitleTextEffects = useMemo(() => {\n    switch (currentSubtitleTextEffects.value) {\n      case 'None':\n        return 'none';\n      case 'Drop Shadow':\n        return 'rgb(34 34 34) 2.2px 2.2px 3.3px, rgb(34 34 34) 2.2px 2.2px 4.4px, rgb(34 34 34) 2.2px 2.2px 5.5px';\n      case 'Raised':\n        return 'rgb(34 34 34) 1.1px 1.1px, rgb(34 34 34) 2.1px 2.1px, rgb(34 34 34) 3.1px 3.1px';\n      case 'Depressed':\n        return 'rgb(204 204 204) 1.1px 1.1px, rgb(34 34 34) -1.1px -1.1px';\n      case 'Outline':\n        return 'rgb(34 34 34) 0px 0px 2.2px, rgb(34 34 34) 0px 0px 2.2px, rgb(34 34 34) 0px 0px 2.2px, rgb(34 34 34) 0px 0px 2.2px, rgb(34 34 34) 0px 0px 2.2px';\n      default:\n        break;\n    }\n  }, [currentSubtitleTextEffects.value]);\n\n  const savePlayProgress = (art: Artplayer) => {\n    if (userId && playerData?.titlePlayer) {\n      switch (typeVideo) {\n        case 'movie':\n          updateHistory(\n            art,\n            fetcher,\n            userId,\n            location.pathname + location.search,\n            'movie',\n            playerData?.titlePlayer,\n            playerData?.overview || '',\n          );\n          break;\n        case 'tv':\n          updateHistory(\n            art,\n            fetcher,\n            userId,\n            location.pathname + location.search,\n            'tv',\n            playerData?.titlePlayer,\n            playerData?.overview || '',\n            seasonId,\n            episodeId,\n          );\n          break;\n        case 'anime':\n          updateHistory(\n            art,\n            fetcher,\n            userId,\n            location.pathname + location.search,\n            'anime',\n            playerData?.titlePlayer,\n            playerData?.overview || '',\n            episodeId,\n          );\n          break;\n        default:\n      }\n    }\n  };\n\n  useEffect(() => {\n    setIsMini(shouldPlayInBackground);\n  }, [shouldPlayInBackground]);\n\n  useEffect(() => {\n    if (playerSettings?.shouldShowPlayer && playerData && !shouldPlayInBackground)\n      setShouldShowPlayer(true);\n    else if (!playerSettings && shouldPlayInBackground && playerData?.sources && !isMobileOnly)\n      setShouldShowPlayer(true);\n    else setShouldShowPlayer(false);\n  }, [playerSettings, playerData, userId]);\n\n  const x = useMotionValue(0);\n  const y = useMotionValue(0);\n  useEffect(() => {\n    if (shouldPlayInBackground && !isMobile) return;\n    x.set(0);\n    y.set(0);\n  }, [x, y, shouldPlayInBackground]);\n\n  const RouteData: PlayerData = useRouteLoaderData(matchesFiltered?.id as string) as PlayerData;\n  useEffect(() => {\n    if (RouteData) {\n      setPlayerData(RouteData);\n    }\n  }, [matchesFiltered?.pathname]);\n\n  const nextEpisodeUrl = useMemo(() => {\n    if (typeVideo === 'tv') {\n      return `/tv-shows/${id}/season/${seasonId}/episode/${\n        currentEpisode + 1\n      }/watch?provider=${provider}&id=${idProvider}`;\n    }\n    if (typeVideo === 'anime') {\n      return `/anime/${id}/episode/${\n        currentEpisode + 1\n      }/watch?provider=${provider}&id=${idProvider}&skipOpEd=${isShowSkipOpEdButton.value}`;\n    }\n  }, [typeVideo, id, seasonId, currentEpisode, provider, idProvider, isShowSkipOpEdButton.value]);\n\n  const prevEpisodeUrl = useMemo(() => {\n    if (currentEpisode > 1) {\n      if (typeVideo === 'tv') {\n        return `/tv-shows/${id}/season/${seasonId}/episode/${\n          currentEpisode - 1\n        }/watch?provider=${provider}&id=${idProvider}`;\n      }\n      if (typeVideo === 'anime') {\n        return `/anime/${id}/episode/${\n          currentEpisode - 1\n        }/watch?provider=${provider}&id=${idProvider}&skipOpEd=${isShowSkipOpEdButton.value}`;\n      }\n    }\n  }, [typeVideo, id, seasonId, currentEpisode, provider, idProvider, isShowSkipOpEdButton.value]);\n\n  useEffect(() => {\n    if (\n      isVideoEnded &&\n      provider &&\n      idProvider &&\n      hasNextEpisode &&\n      nextEpisodeUrl &&\n      typeVideo !== 'movie' &&\n      isAutoPlayNextEpisode.value\n    ) {\n      navigate(nextEpisodeUrl);\n    }\n  }, [isVideoEnded]);\n\n  const [isWatchTrailerDialogVisible, setWatchTrailerDialogVisible] = useState(false);\n  const [trailer, setTrailer] = useState<Trailer>({});\n  useEffect(() => {\n    if (fetcher.data && (fetcher.data as { videos: IVideos }).videos) {\n      const { results } = (fetcher.data as { videos: IVideos }).videos;\n      const officialTrailer = results.find((result: Trailer) => result.type === 'Trailer');\n      setTrailer(officialTrailer || {});\n    }\n  }, [fetcher.data]);\n\n  useEffect(() => {\n    if (playerData) {\n      if (provider && sources && sources.length > 0) {\n        setQualitySelector(\n          provider === 'Loklok' ||\n            provider === 'Gogo' ||\n            provider === 'Zoro' ||\n            provider === 'Flixhq'\n            ? sources?.map(({ quality, url }: { quality: number | string; url: string }) => ({\n                html: quality.toString(),\n                url,\n                isM3U8: true,\n                isDASH: false,\n                ...(provider === 'Flixhq' && quality === 'auto' && { default: true }),\n                ...(provider === 'Loklok' && Number(quality) === 720 && { default: true }),\n                ...((provider === 'Gogo' || provider === 'Zoro') &&\n                  quality === 'default' && { default: true }),\n              }))\n            : provider === 'Bilibili'\n            ? sources?.map(({ quality, url }: { quality: number | string; url: string }) => ({\n                html: quality.toString(),\n                url: url.toString(),\n                isM3U8: false,\n                isDASH: true,\n                ...(quality === 'auto' && { default: true }),\n              }))\n            : provider === 'KissKh'\n            ? sources?.map(({ quality, url }: { quality: number | string; url: string }) => ({\n                html: quality.toString(),\n                url,\n                isM3U8: false,\n                isDASH: true,\n                ...(quality === 'auto' && { default: true }),\n              }))\n            : provider === 'test'\n            ? sources?.map(({ quality, url }: { quality: number | string; url: string }) => ({\n                html: quality.toString(),\n                url: url.toString(),\n                isM3U8: true,\n                isDASH: false,\n                ...(quality === 720 && { default: true }),\n              }))\n            : sources?.map(({ quality, url }: { quality: number | string; url: string }) => ({\n                html: quality.toString(),\n                url: url.toString(),\n                isM3U8: true,\n                isDASH: false,\n                ...(quality === 'default' && { default: true }),\n              })),\n        );\n        setSubtitleSelector(\n          subtitles?.map(({ lang, url }: { lang: string; url: string }) => ({\n            html: lang.toString(),\n            url: url.toString(),\n            ...(provider === 'Flixhq' && lang === 'English' && { default: true }),\n            ...(provider === 'Loklok' && lang.includes('en') && { default: true }),\n            ...(provider === 'Bilibili' && lang.includes('en') && { default: true }),\n            ...(provider === 'KissKh' && lang === 'English' && { default: true }),\n            ...(provider === 'test' && lang === 'ch-jp' && { default: true }),\n          })),\n        );\n        setRoutePlayer(playerData?.routePlayer);\n        setTitlePlayer(\n          `${playerData?.titlePlayer}${seasonId ? ` season ${seasonId}` : ''}${\n            episodeId ? ` episode ${episodeId}` : ''\n          }`,\n        );\n      }\n    }\n  }, [playerData]);\n\n  return (\n    <div className=\"w-full\" style={{ margin: 0, padding: 0, width: isMini ? '20rem' : '100%' }}>\n      <div className=\"pointer-events-none fixed inset-0\" ref={constraintsRef} />\n      <AnimatePresence initial={false}>\n        {shouldShowPlayer ? (\n          <motion.div\n            layout\n            ref={ref}\n            drag={shouldPlayInBackground}\n            dragConstraints={constraintsRef}\n            dragElastic={0.1}\n            dragMomentum={false}\n            dragTransition={{ bounceStiffness: 600, bounceDamping: 10 }}\n            initial={{ opacity: 0, x: 0 }}\n            animate={{ opacity: 1, x: 0 }}\n            exit={{ opacity: 0, x: 250 }}\n            transition={{ duration: 0.3 }}\n            className={\n              shouldPlayInBackground\n                ? 'fixed bottom-16 right-4 z-[9999]'\n                : isShowOverlay\n                ? ''\n                : isMobile\n                ? 'relative z-[4000]'\n                : ''\n            }\n            style={{\n              x,\n              y,\n              width: isMini ? '25rem' : '100%',\n              height: isMini ? '14.0625rem' : '100%',\n            }}\n          >\n            {playerData?.sources ? (\n              <>\n                <Player\n                  key={`${id}-${routePlayer}-${titlePlayer}-${provider}`}\n                  option={{\n                    id: `${id}-${routePlayer}-${titlePlayer}-${provider}`,\n                    type: provider === 'Bilibili' ? 'mpd' : provider === 'test' ? 'mp4' : 'm3u8',\n                    autoSize: isAutoSize.value,\n                    loop: isLoop.value,\n                    muted: isMuted.value,\n                    mutex: true,\n                    setting: false,\n                    flip: true,\n                    playbackRate: true,\n                    aspectRatio: true,\n                    fullscreen: true,\n                    fullscreenWeb: false,\n                    airplay: true,\n                    pip: isPicInPic.value,\n                    autoplay: isAutoPlay.value,\n                    screenshot: isDesktop ? isScreenshot.value : false,\n                    subtitleOffset: true,\n                    fastForward: isMobile ? isFastForward.value : false,\n                    lock: isMobile,\n                    miniProgressBar: isMiniProgressBar.value,\n                    autoOrientation: isMobile,\n                    isLive: false,\n                    playsInline: true,\n                    autoPlayback: isAutoPlayback.value,\n                    theme: 'hsl(var(--theme-primary))',\n                    autoMini: isAutoMini.value,\n                    hotkey: true,\n                    useSSR: false,\n                    moreVideoAttr: isDesktop\n                      ? {\n                          crossOrigin: 'anonymous',\n                        }\n                      : {\n                          'x5-video-player-type': 'h5',\n                          'x5-video-player-fullscreen': false,\n                          'x5-video-orientation': 'portrait',\n                          preload: 'metadata',\n                        },\n                    url:\n                      provider === 'Loklok'\n                        ? sources?.find(\n                            (item: { quality: number | string; url: string }) =>\n                              Number(item.quality) === 720,\n                          )?.url ||\n                          (sources && sources[0]?.url)\n                        : provider === 'Flixhq'\n                        ? sources?.find(\n                            (item: { quality: number | string; url: string }) =>\n                              item.quality === 'auto',\n                          )?.url ||\n                          (sources && sources[0]?.url)\n                        : provider === 'Gogo' || provider === 'Zoro'\n                        ? sources?.find(\n                            (item: { quality: number | string; url: string }) =>\n                              item.quality === 'default',\n                          )?.url ||\n                          (sources && sources[0]?.url)\n                        : provider === 'Bilibili'\n                        ? sources && sources[0]?.url\n                        : provider === 'KissKh'\n                        ? sources && sources[0]?.url\n                        : provider === 'test'\n                        ? sources?.find((source) => Number(source.quality) === 720)?.url\n                        : sources?.find(\n                            (item: { quality: number | string; url: string }) =>\n                              item.quality === 'default',\n                          )?.url ||\n                          (sources && sources[0]?.url) ||\n                          '',\n                    subtitle: {\n                      url:\n                        provider === 'Loklok'\n                          ? subtitles?.find((item: { lang: string; url: string }) =>\n                              item.lang.includes('English'),\n                            )?.url\n                          : provider === 'Flixhq'\n                          ? subtitles?.find((item: { lang: string; url: string }) =>\n                              item.lang.includes('English'),\n                            )?.url || ''\n                          : provider === 'KissKh'\n                          ? subtitles?.find(\n                              (item: { lang: string; url: string; default?: boolean }) =>\n                                item.default,\n                            )?.url || ''\n                          : provider === 'test'\n                          ? subtitles?.find((item: { lang: string; url: string }) =>\n                              item.lang.includes('ch-jp'),\n                            )?.url || ''\n                          : subtitles?.find((item: { lang: string; url: string }) =>\n                              item.lang.includes('English'),\n                            )?.url || '',\n                      encoding: 'utf-8',\n                      type:\n                        provider === 'Flixhq' || provider === 'Loklok' || provider === 'Bilibili'\n                          ? 'vtt'\n                          : provider === 'KissKh'\n                          ? 'srt'\n                          : '',\n                    },\n                    poster: posterPlayer,\n                    layers: [\n                      {\n                        html: '',\n                        name: 'mask',\n                        style: {\n                          position: 'absolute',\n                          top: 0,\n                          left: 0,\n                          width: '100%',\n                          height: '100%',\n                        },\n                      },\n                      {\n                        html: '',\n                        name: 'playPauseButton',\n                        style: {\n                          position: 'absolute',\n                          top: 0,\n                          left: 0,\n                          width: '100%',\n                          height: '100%',\n                        },\n                      },\n                      {\n                        html: '',\n                        name: 'miniTopControlButtons',\n                        style: {\n                          position: 'absolute',\n                          top: 0,\n                          left: 0,\n                          width: '100%',\n                        },\n                      },\n                      {\n                        html: '',\n                        name: 'skipButton',\n                        style: {\n                          position: 'absolute',\n                          bottom: '65px',\n                          right: '10px',\n                        },\n                      },\n                    ],\n                    icons: {\n                      loading: `<div class=\"w-12 h-12 !m-auto relative before:content-[''] before:w-12 before:h-[5px] before:bg-primary-700 before:absolute before:top-[60px] before:left-0 before:rounded-[50%] before:animate-shadow after:content-[''] after:w-full after:h-full after:bg-primary after:absolute after:top-0 after:left-0 after:rounded-small after:animate-jump\"></div>`,\n                      play: `\n                        <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n                          <path d=\"M17.49 9.59965L5.6 16.7696C4.9 17.1896 4 16.6896 4 15.8696V7.86965C4 4.37965 7.77 2.19965 10.8 3.93965L15.39 6.57965L17.48 7.77965C18.17 8.18965 18.18 9.18965 17.49 9.59965Z\" fill=\"currentColor\"/>\n                          <path d=\"M18.0888 15.4606L14.0388 17.8006L9.99883 20.1306C8.54883 20.9606 6.90883 20.7906 5.71883 19.9506C5.13883 19.5506 5.20883 18.6606 5.81883 18.3006L18.5288 10.6806C19.1288 10.3206 19.9188 10.6606 20.0288 11.3506C20.2788 12.9006 19.6388 14.5706 18.0888 15.4606Z\" fill=\"currentColor\"/>\n                        </svg>\n                      `,\n                      pause: `\n                        <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n                          <path d=\"M10.65 19.11V4.89C10.65 3.54 10.08 3 8.64 3H5.01C3.57 3 3 3.54 3 4.89V19.11C3 20.46 3.57 21 5.01 21H8.64C10.08 21 10.65 20.46 10.65 19.11Z\" fill=\"currentColor\"/>\n                          <path d=\"M21.0016 19.11V4.89C21.0016 3.54 20.4316 3 18.9916 3H15.3616C13.9316 3 13.3516 3.54 13.3516 4.89V19.11C13.3516 20.46 13.9216 21 15.3616 21H18.9916C20.4316 21 21.0016 20.46 21.0016 19.11Z\" fill=\"currentColor\"/>\n                        </svg>\n                      `,\n                      volume: `\n                        <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n                          <path d=\"M18.0003 16.7503C17.8403 16.7503 17.6903 16.7003 17.5503 16.6003C17.2203 16.3503 17.1503 15.8803 17.4003 15.5503C18.9703 13.4603 18.9703 10.5403 17.4003 8.45027C17.1503 8.12027 17.2203 7.65027 17.5503 7.40027C17.8803 7.15027 18.3503 7.22027 18.6003 7.55027C20.5603 10.1703 20.5603 13.8303 18.6003 16.4503C18.4503 16.6503 18.2303 16.7503 18.0003 16.7503Z\" fill=\"currentColor\"/>\n                          <path d=\"M19.8284 19.2503C19.6684 19.2503 19.5184 19.2003 19.3784 19.1003C19.0484 18.8503 18.9784 18.3803 19.2284 18.0503C21.8984 14.4903 21.8984 9.51027 19.2284 5.95027C18.9784 5.62027 19.0484 5.15027 19.3784 4.90027C19.7084 4.65027 20.1784 4.72027 20.4284 5.05027C23.4984 9.14027 23.4984 14.8603 20.4284 18.9503C20.2884 19.1503 20.0584 19.2503 19.8284 19.2503Z\" fill=\"currentColor\"/>\n                          <path d=\"M14.02 3.78168C12.9 3.16168 11.47 3.32168 10.01 4.23168L7.09 6.06168C6.89 6.18168 6.66 6.25168 6.43 6.25168H5.5H5C2.58 6.25168 1.25 7.58168 1.25 10.0017V14.0017C1.25 16.4217 2.58 17.7517 5 17.7517H5.5H6.43C6.66 17.7517 6.89 17.8217 7.09 17.9417L10.01 19.7717C10.89 20.3217 11.75 20.5917 12.55 20.5917C13.07 20.5917 13.57 20.4717 14.02 20.2217C15.13 19.6017 15.75 18.3117 15.75 16.5917V7.41168C15.75 5.69168 15.13 4.40168 14.02 3.78168Z\" fill=\"currentColor\"/>\n                        </svg>\n                      `,\n                      volumeClose: `\n                        <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n                          <path d=\"M22.5314 13.4197L21.0814 11.9697L22.4814 10.5697C22.7714 10.2797 22.7714 9.79969 22.4814 9.50969C22.1914 9.21969 21.7114 9.21969 21.4214 9.50969L20.0214 10.9097L18.5714 9.45969C18.2814 9.16969 17.8014 9.16969 17.5114 9.45969C17.2214 9.74969 17.2214 10.2297 17.5114 10.5197L18.9614 11.9697L17.4714 13.4597C17.1814 13.7497 17.1814 14.2297 17.4714 14.5197C17.6214 14.6697 17.8114 14.7397 18.0014 14.7397C18.1914 14.7397 18.3814 14.6697 18.5314 14.5197L20.0214 13.0297L21.4714 14.4797C21.6214 14.6297 21.8114 14.6997 22.0014 14.6997C22.1914 14.6997 22.3814 14.6297 22.5314 14.4797C22.8214 14.1897 22.8214 13.7197 22.5314 13.4197Z\" fill=\"currentColor\"/>\n                          <path d=\"M14.02 3.78168C12.9 3.16168 11.47 3.32168 10.01 4.23168L7.09 6.06168C6.89 6.18168 6.66 6.25168 6.43 6.25168H5.5H5C2.58 6.25168 1.25 7.58168 1.25 10.0017V14.0017C1.25 16.4217 2.58 17.7517 5 17.7517H5.5H6.43C6.66 17.7517 6.89 17.8217 7.09 17.9417L10.01 19.7717C10.89 20.3217 11.75 20.5917 12.55 20.5917C13.07 20.5917 13.57 20.4717 14.02 20.2217C15.13 19.6017 15.75 18.3117 15.75 16.5917V7.41168C15.75 5.69168 15.13 4.40168 14.02 3.78168Z\" fill=\"currentColor\"/>\n                        </svg>\n                      `,\n                      screenshot: `\n                        <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n                          <path d=\"M18.0002 6C17.3902 6 16.8302 5.65 16.5502 5.11L15.8302 3.66C15.3702 2.75 14.1702 2 13.1502 2H10.8602C9.83017 2 8.63017 2.75 8.17017 3.66L7.45017 5.11C7.17017 5.65 6.61017 6 6.00017 6C3.83017 6 2.11017 7.83 2.25017 9.99L2.77017 18.25C2.89017 20.31 4.00017 22 6.76017 22H17.2402C20.0002 22 21.1002 20.31 21.2302 18.25L21.7502 9.99C21.8902 7.83 20.1702 6 18.0002 6ZM10.5002 7.25H13.5002C13.9102 7.25 14.2502 7.59 14.2502 8C14.2502 8.41 13.9102 8.75 13.5002 8.75H10.5002C10.0902 8.75 9.75017 8.41 9.75017 8C9.75017 7.59 10.0902 7.25 10.5002 7.25ZM12.0002 18.12C10.1402 18.12 8.62017 16.61 8.62017 14.74C8.62017 12.87 10.1302 11.36 12.0002 11.36C13.8702 11.36 15.3802 12.87 15.3802 14.74C15.3802 16.61 13.8602 18.12 12.0002 18.12Z\" fill=\"currentColor\"/>\n                        </svg>\n                      `,\n                      pip: `\n                        <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n                          <path d=\"M16.19 2H7.81C4.17 2 2 4.17 2 7.81V16.18C2 19.83 4.17 22 7.81 22H16.18C19.82 22 21.99 19.83 21.99 16.19V7.81C22 4.17 19.83 2 16.19 2ZM18.5 16.4C18.5 17.9 17.9 18.5 16.4 18.5H12.6C11.1 18.5 10.5 17.9 10.5 16.4V14.6C10.5 13.1 11.1 12.5 12.6 12.5H16.4C17.9 12.5 18.5 13.1 18.5 14.6V16.4Z\" fill=\"currentColor\"/>\n                        </svg>\n                      `,\n                      fullscreenOn: `\n                        <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n                          <path d=\"M16.19 2H7.81C4.17 2 2 4.17 2 7.81V16.18C2 19.83 4.17 22 7.81 22H16.18C19.82 22 21.99 19.83 21.99 16.19V7.81C22 4.17 19.83 2 16.19 2ZM10.22 21H7.91C5.2 21 3 18.8 3 16.09V13.78C3 13.37 3.34 13.03 3.75 13.03C4.16 13.03 4.5 13.37 4.5 13.78V16.09C4.5 17.97 6.03 19.5 7.91 19.5H10.22C10.63 19.5 10.97 19.84 10.97 20.25C10.97 20.66 10.64 21 10.22 21ZM10.22 4.5H7.91C6.03 4.5 4.5 6.03 4.5 7.91V10.22C4.5 10.63 4.16 10.97 3.75 10.97C3.34 10.97 3 10.64 3 10.22V7.91C3 5.2 5.2 3 7.91 3H10.22C10.63 3 10.97 3.34 10.97 3.75C10.97 4.16 10.64 4.5 10.22 4.5ZM21 16.09C21 18.8 18.8 21 16.09 21H14.7C14.29 21 13.95 20.66 13.95 20.25C13.95 19.84 14.29 19.5 14.7 19.5H16.09C17.97 19.5 19.5 17.97 19.5 16.09V14.7C19.5 14.29 19.84 13.95 20.25 13.95C20.66 13.95 21 14.29 21 14.7V16.09ZM21 10.22C21 10.63 20.66 10.97 20.25 10.97C19.84 10.97 19.5 10.63 19.5 10.22V7.91C19.5 6.03 17.97 4.5 16.09 4.5H13.78C13.37 4.5 13.03 4.16 13.03 3.75C13.03 3.34 13.36 3 13.78 3H16.09C18.8 3 21 5.2 21 7.91V10.22Z\" fill=\"currentColor\"/>\n                        </svg>\n                      `,\n                      fullscreenOff: `\n                        <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n                          <path d=\"M3 9C3 8.44772 3.44772 8 4 8H8L8 4C8 3.44772 8.44772 3 9 3C9.55229 3 10 3.44772 10 4L10 9C10 9.55229 9.55228 10 9 10H4C3.44772 10 3 9.55229 3 9Z\" fill=\"currentColor\"/>\n                          <path d=\"M20 8C20.5523 8 21 8.44772 21 9C21 9.55229 20.5523 10 20 10H15C14.4477 10 14 9.55229 14 9L14 4C14 3.44772 14.4477 3 15 3C15.5523 3 16 3.44772 16 4V8H20Z\" fill=\"currentColor\"/>\n                          <path d=\"M20 16C20.5523 16 21 15.5523 21 15C21 14.4477 20.5523 14 20 14H15C14.4477 14 14 14.4477 14 15L14 20C14 20.5523 14.4477 21 15 21C15.5523 21 16 20.5523 16 20V16H20Z\" fill=\"currentColor\"/>\n                          <path d=\"M4 16C3.44772 16 3 15.5523 3 15C3 14.4477 3.44772 14 4 14H9C9.55228 14 10 14.4477 10 15L10 20C10 20.5523 9.55229 21 9 21C8.44772 21 8 20.5523 8 20L8 16H4Z\" fill=\"currentColor\"/>\n                        </svg>\n                      `,\n                    },\n                    customType:\n                      provider === 'Bilibili'\n                        ? {\n                            mpd: async (video: HTMLMediaElement, url: string) => {\n                              const { default: dashjs } = await import('dashjs');\n                              const player = dashjs.MediaPlayer().create();\n                              player.initialize(video, url, false);\n                            },\n                          }\n                        : {\n                            m3u8: async (video: HTMLMediaElement, url: string) => {\n                              if (hls) {\n                                hls.destroy();\n                              }\n                              if (Hls.isSupported()) {\n                                hls = new Hls();\n                                hls.loadSource(url);\n                                hls.attachMedia(video);\n                              } else {\n                                const canPlay = video.canPlayType('application/vnd.apple.mpegurl');\n                                if (canPlay === 'probably' || canPlay === 'maybe') {\n                                  video.src = url;\n                                }\n                              }\n                            },\n                          },\n                    controls: [\n                      {\n                        position: 'right',\n                        name: 'settings',\n                        html: '',\n                        tooltip: 'Settings',\n                      },\n                      ...(currentEpisode > 1 && isDesktop\n                        ? [{ position: 'left', name: 'prev', html: '' }]\n                        : []),\n                      ...(hasNextEpisode && isDesktop\n                        ? [{ position: 'left', name: 'next', html: '' }]\n                        : []),\n                    ],\n                    cssVar: {\n                      '--art-font-color': 'hsl(var(--theme-default-foreground))',\n                      '--art-progress-color': 'hsl(var(--theme-default-foreground) / 0.2)',\n                      '--art-loaded-color': 'hsl(var(--theme-default-foreground) / 0.4)',\n                    },\n                  }}\n                  style={{\n                    // @ts-ignore\n                    '--art-subtitle-color': subtitleColor,\n                    '--art-subtitle-background-color': subtitleBackgroundColor,\n                    '--art-subtitle-window-color': subtitleWindowColor,\n                    '--art-subtitle-custom-font-size': subtitleFontSize,\n                    '--art-subtitle-text-shadow': subtitleTextEffects,\n                  }}\n                  getInstance={(art) => {\n                    art.on('ready', async () => {\n                      const t = new URLSearchParams(location.search).get('t');\n                      if (t) {\n                        art.currentTime = Number(t);\n                      }\n                      if (art?.height) {\n                        art.controls.add({\n                          position: 'right',\n                          name: 'topControlButtons',\n                          html: '',\n                          style: {\n                            position: 'absolute',\n                            bottom: `${Number(art?.height) - (isMobile ? 70 : 50)}px`,\n                            left: isMobile ? '0' : '-10px',\n                            width: `${Number(art?.width)}px`,\n                            padding: '0 7px 0 7px',\n                            height: '55px',\n                            cursor: 'default',\n                            zIndex: '50',\n                          },\n                        });\n                      }\n                      setIsVideoEnded(false);\n                      setShowSkipButton(false);\n                      setArtplayer(art);\n                      if (autoShowSubtitle.value && art.subtitle) {\n                        art.subtitle.show = autoShowSubtitle.value;\n                      }\n                      PlayerHotKey(art, setShowSubtitle);\n                    });\n                    savePlayProgress(art);\n                    art.on('play', () => {\n                      setIsVideoEnded(false);\n                      setIsPlayerPlaying(true);\n                    });\n                    art.on('pause', () => {\n                      setIsPlayerPlaying(false);\n                    });\n                    art.on('video:loadedmetadata', () => {\n                      /* Adding highlights in player's progress bar */\n                      if (highlights) {\n                        const $highlight = art.query('.art-progress-highlight');\n                        // @ts-ignore\n                        const { append, createElement, setStyles } = art.constructor.utils;\n                        for (let index = 0; index < highlights.length; index += 1) {\n                          const item = highlights[index];\n                          const left = (item.start / art.duration) * 100;\n                          const width = ((item.end - item.start) / art.duration) * 100;\n                          const $item = createElement('span');\n                          $item.dataset.text = item.text;\n                          setStyles($item, {\n                            left: `${left}%`,\n                            width: `${width}%`,\n                            backgroundColor: 'hsl(var(--theme-secondary)) !important',\n                          });\n                          append($highlight, $item);\n                        }\n                      }\n                    });\n                    art.on('resize', () => {\n                      // eslint-disable-next-line @typescript-eslint/dot-notation\n                      const $topControlButtons = art.controls['topControlButtons'];\n                      if ($topControlButtons?.style) {\n                        // set top control buttons position when player resize\n                        $topControlButtons.style.bottom = `${\n                          Number(art?.height) - (isMobile && !art.fullscreen ? 70 : 55)\n                        }px`;\n                        $topControlButtons.style.width = `${Number(art?.width)}px`;\n                      }\n                    });\n                    art.on('video:timeupdate', () => {\n                      /* Finding the current highlight and show skip button */\n                      if (highlights) {\n                        const findCurrentHighlight = highlights.find(\n                          (item) => art.currentTime >= item.start && art.currentTime <= item.end,\n                        );\n                        if (findCurrentHighlight) {\n                          if (isAutoSkipOpEd.value) {\n                            art.currentTime = findCurrentHighlight.end;\n                            art.notice.show = `Skipped ${findCurrentHighlight.text}`;\n                          } else {\n                            setShowSkipButton(true);\n                            setCurrentHighlight(findCurrentHighlight);\n                          }\n                        } else {\n                          setShowSkipButton(false);\n                          setCurrentHighlight(null);\n                        }\n                      }\n                    });\n                    art.on('fullscreen', (state) => {\n                      setIsPlayerFullScreen(state);\n                    });\n                    art.on('video:ended', () => {\n                      setIsVideoEnded(true);\n                      setIsPlayerPlaying(false);\n                      setShowSkipButton(false);\n                      art.fullscreen = false;\n                    });\n                    art.on('destroy', () => {\n                      setIsVideoEnded(false);\n                      setIsPlayerPlaying(false);\n                      setIsPlayerFullScreen(false);\n                      setArtplayer(null);\n                      if (hls) {\n                        hls.destroy();\n                      }\n                    });\n                    art.on('error', (_error, _reconnectTime) => {\n                      toast.error('An error occurred while playing the video.', {\n                        description: 'Please try again later.',\n                      });\n                    });\n                  }}\n                  setIsPlayerPlaying={setIsPlayerPlaying}\n                  className={playerStyles({\n                    isMini,\n                    isSettingsOpen,\n                    isMobile,\n                    isPlayerFullScreen,\n                    showSubtitle,\n                    isShowOverlay,\n                  })}\n                />\n                {!isMini ? (\n                  <div className=\"mt-6 flex flex-row flex-wrap items-center justify-start gap-4\">\n                    <Button\n                      type=\"button\"\n                      size=\"sm\"\n                      variant={isShowOverlay ? 'flat' : 'solid'}\n                      className={isShowOverlay ? 'z-[9999] mb-3' : 'mb-3'}\n                      onPress={() => setIsShowOverlay(!isShowOverlay)}\n                    >\n                      Toggle Light\n                    </Button>\n                    <Dialog\n                      open={isWatchTrailerDialogVisible}\n                      onOpenChange={setWatchTrailerDialogVisible}\n                    >\n                      <DialogTrigger asChild>\n                        <Button\n                          type=\"button\"\n                          size=\"sm\"\n                          onPress={() => {\n                            artplayer?.pause();\n                            if (typeVideo === 'movie' || typeVideo === 'tv')\n                              fetcher.load(\n                                `/${typeVideo === 'movie' ? 'movies' : 'tv-shows'}/${id}/videos`,\n                              );\n                          }}\n                          className=\"mb-3\"\n                        >\n                          Watch Trailer\n                        </Button>\n                      </DialogTrigger>\n                      <DialogContent className=\"overflow-hidden !p-0\">\n                        {typeVideo === 'movie' || typeVideo === 'tv' ? (\n                          <WatchTrailer trailer={trailer} />\n                        ) : null}\n                        {typeVideo === 'anime' && trailerAnime ? (\n                          <WatchTrailer trailer={trailerAnime} />\n                        ) : null}\n                      </DialogContent>\n                    </Dialog>\n                  </div>\n                ) : null}\n              </>\n            ) : (\n              <PlayerError\n                title=\"Video not found\"\n                message=\"The video you are trying to watch is not available.\"\n              />\n            )}\n          </motion.div>\n        ) : null}\n      </AnimatePresence>\n      {/* Creating portal for the player layers */}\n      {isMini && artplayer\n        ? createPortal(<div className=\"z-[1] h-full w-full\" />, artplayer.layers.mask)\n        : null}\n      {isMini && artplayer\n        ? createPortal(\n            <div className=\"flex h-full w-full flex-row items-center justify-center\">\n              <Tooltip content={isPlayerPlaying ? 'Pause' : 'Play'} showArrow closeDelay={0}>\n                <Button\n                  type=\"button\"\n                  variant=\"light\"\n                  onPress={() => {\n                    if (isPlayerPlaying) {\n                      artplayer.pause();\n                    } else {\n                      artplayer.play();\n                    }\n                  }}\n                  isIconOnly\n                  className=\"h-12 data-[hover=true]:bg-transparent\"\n                >\n                  {isPlayerPlaying ? (\n                    <Pause height={48} width={48} filled />\n                  ) : (\n                    <Play height={48} width={48} filled />\n                  )}\n                </Button>\n              </Tooltip>\n            </div>,\n            artplayer.layers.playPauseButton,\n          )\n        : null}\n      {isMini && artplayer\n        ? createPortal(\n            <div className=\"flex flex-row items-center justify-between\">\n              <div className=\"flex flex-row items-center justify-center gap-x-1\">\n                <Tooltip content=\"Expand\" showArrow closeDelay={0}>\n                  <Button\n                    type=\"button\"\n                    variant=\"light\"\n                    onPress={() => navigate(routePlayer)}\n                    isIconOnly\n                    className=\"data-[hover=true]:bg-transparent\"\n                  >\n                    <Expand filled />\n                  </Button>\n                </Tooltip>\n              </div>\n              <PlayerSettings\n                artplayer={artplayer}\n                qualitySelector={qualitySelector}\n                subtitleSelector={subtitleSelector}\n                isPlayerFullScreen={isPlayerFullScreen}\n                isSettingsOpen={isSettingsOpen}\n                showSubtitle={showSubtitle}\n                setShowSubtitle={setShowSubtitle}\n                setSettingsOpen={setSettingsOpen}\n                subtitleOptions={subtitleOptions}\n              />\n            </div>,\n            artplayer.layers.miniTopControlButtons,\n          )\n        : null}\n      {!isMini && artplayer && showSkipButton\n        ? createPortal(\n            <motion.div\n              initial={{ opacity: 0, x: 40 }}\n              animate={{ opacity: 1, x: 0 }}\n              transition={{ duration: 0.4 }}\n            >\n              <Button\n                type=\"button\"\n                onPress={() => {\n                  if (currentHighlight?.end) {\n                    artplayer.currentTime = currentHighlight?.end;\n                  }\n                }}\n              >\n                {`Skip ${currentHighlight?.text || ''}`}\n              </Button>\n            </motion.div>,\n            artplayer.layers.skipButton,\n          )\n        : null}\n      {/* Creating portals for the player controls */}\n      {artplayer && !isMini && isDesktop\n        ? createPortal(\n            <PlayerSettings\n              artplayer={artplayer}\n              qualitySelector={qualitySelector}\n              subtitleSelector={subtitleSelector}\n              isPlayerFullScreen={isPlayerFullScreen}\n              isSettingsOpen={isSettingsOpen}\n              showSubtitle={showSubtitle}\n              setShowSubtitle={setShowSubtitle}\n              setSettingsOpen={setSettingsOpen}\n              subtitleOptions={subtitleOptions}\n            />,\n            artplayer.controls.settings,\n          )\n        : null}\n      {artplayer?.controls.prev && !isMini && currentEpisode > 1 && isDesktop\n        ? createPortal(\n            <Button\n              type=\"button\"\n              variant=\"light\"\n              onPress={() => prevEpisodeUrl && navigate(prevEpisodeUrl)}\n              isIconOnly\n              className=\"art-icon data-[hover=true]:bg-transparent\"\n            >\n              <Previous filled />\n            </Button>,\n            artplayer.controls.prev,\n          )\n        : null}\n      {artplayer?.controls.next && !isMini && hasNextEpisode && isDesktop\n        ? createPortal(\n            <Button\n              type=\"button\"\n              variant=\"light\"\n              onPress={() => nextEpisodeUrl && navigate(nextEpisodeUrl)}\n              isIconOnly\n              className=\"art-icon data-[hover=true]:bg-transparent\"\n            >\n              <Next filled />\n            </Button>,\n            artplayer.controls.next,\n          )\n        : null}\n      {artplayer?.controls.topControlButtons && !isMini\n        ? createPortal(\n            <div className=\"relative z-10 flex w-full flex-row items-center justify-start gap-x-2\">\n              <div className=\"flex w-2/3 shrink grow-0 basis-2/3 flex-row items-center justify-start gap-x-2\">\n                {isPlayerFullScreen ? (\n                  <div className=\"flex w-full flex-col items-start justify-center\">\n                    <h6 className=\"w-full truncate text-start !text-default-foreground\">\n                      {playerData?.titlePlayer}\n                    </h6>\n                    <p className=\"!text-default-foreground/80\">\n                      {seasonId ? ` Season ${seasonId}` : ''}\n                      {episodeId ? ` Episode ${episodeId}` : ''}\n                    </p>\n                  </div>\n                ) : null}\n              </div>\n              {isMobile ? (\n                <div className=\"flex shrink-0 grow basis-1/3 flex-row items-center justify-end gap-x-2\">\n                  <PlayerSettings\n                    artplayer={artplayer}\n                    qualitySelector={qualitySelector}\n                    subtitleSelector={subtitleSelector}\n                    isPlayerFullScreen={isPlayerFullScreen}\n                    isSettingsOpen={isSettingsOpen}\n                    showSubtitle={showSubtitle}\n                    setShowSubtitle={setShowSubtitle}\n                    setSettingsOpen={setSettingsOpen}\n                    subtitleOptions={subtitleOptions}\n                  />\n                </div>\n              ) : null}\n            </div>,\n            artplayer.controls.topControlButtons,\n          )\n        : null}\n    </div>\n  );\n};\n\nexport default GlobalPlayer;\n"
  },
  {
    "path": "app/components/layouts/Header.tsx",
    "content": "import type { User } from '@supabase/supabase-js';\nimport { motion, useTransform } from 'framer-motion';\nimport { tv } from 'tailwind-variants';\n\nimport { useHeaderOptions } from '~/utils/react/hooks/useHeader';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport { useHeaderStyle } from '~/store/layout/useHeaderStyle';\nimport { useLayout } from '~/store/layout/useLayout';\nimport MultiLevelDropdown from '~/components/layouts/MultiLevelDropdown';\nimport ListViewChangeButton from '~/components/elements/shared/ListViewChangeButton';\n\nimport ControlNavigation from './ControlNavigation';\n\ninterface IHeaderProps {\n  user?: User;\n}\n\nconst headerStyles = tv({\n  base: 'fixed z-[1000] hidden h-[64px] w-[100vw] flex-row items-center justify-between gap-x-4 px-5 py-3 sm:flex',\n  variants: {\n    miniSidebar: {\n      true: 'top-0 sm:w-[calc(100vw_-_80px)]',\n    },\n    boxedSidebar: {\n      true: 'top-[15px] sm:w-[calc(100vw_-_282px)]',\n    },\n    hideSidebar: {\n      true: 'top-0 sm:w-[100vw]',\n    },\n  },\n  compoundVariants: [\n    {\n      miniSidebar: true,\n      boxedSidebar: true,\n      hideSidebar: false,\n      class: 'top-[15px] sm:w-[calc(100vw_-_112px)]',\n    },\n    {\n      miniSidebar: false,\n      boxedSidebar: false,\n      hideSidebar: false,\n      class: 'top-0 sm:w-[calc(100vw_-_250px)]',\n    },\n    {\n      boxedSidebar: true,\n      hideSidebar: true,\n      class: 'top-[15px] sm:w-[calc(100vw_-_17px)]',\n    },\n  ],\n  defaultVariants: {\n    miniSidebar: false,\n    boxedSidebar: false,\n    hideSidebar: false,\n  },\n});\n\nconst backgroundColorStyles = tv({\n  base: 'pointer-events-none absolute left-0 top-px z-[-1] w-full backdrop-blur-2xl backdrop-contrast-125 backdrop-saturate-200',\n  variants: {\n    isShowTablink: {\n      true: 'h-[111px]',\n      false: 'h-[64px]',\n    },\n    customBackgroundColor: {\n      true: 'h-[64px]',\n    },\n    boxedSidebar: {\n      true: 'h-[64px] border-b border-divider sm:rounded-t-medium',\n      false: 'h-[64px] border-b border-divider sm:rounded-tl-medium',\n    },\n    miniSidebar: {\n      true: 'h-[64px] border-b border-divider',\n    },\n    hideSidebar: {\n      true: 'h-[64px] border-b border-divider',\n    },\n  },\n  compoundVariants: [\n    {\n      isShowTablink: true,\n      boxedSidebar: true,\n      miniSidebar: true,\n      hideSidebar: false,\n      class: 'h-[119px] border-0',\n    },\n    {\n      isShowTablink: true,\n      boxedSidebar: true,\n      miniSidebar: false,\n      hideSidebar: false,\n      class: 'h-[111px] border-0',\n    },\n    {\n      isShowTablink: true,\n      boxedSidebar: false,\n      hideSidebar: true,\n      class: 'h-[111px] border-0 sm:rounded-none',\n    },\n    {\n      isShowTablink: true,\n      boxedSidebar: true,\n      hideSidebar: true,\n      class: 'h-[119px] border-0',\n    },\n    {\n      isShowTablink: true,\n      boxedSidebar: false,\n      hideSidebar: false,\n      class: 'h-[111px] border-0',\n    },\n    {\n      isShowTablink: false,\n      customBackgroundColor: true,\n      class: 'h-[64px] border-0',\n    },\n  ],\n  defaultVariants: {\n    isShowTablink: false,\n    boxedSidebar: false,\n    miniSidebar: false,\n    hideSidebar: false,\n    customBackgroundColor: false,\n  },\n});\n\nconst Header: React.FC<IHeaderProps> = (props: IHeaderProps) => {\n  const { user } = props;\n  const { sidebarMiniMode, sidebarBoxedMode } = useSoraSettings();\n  const { scrollY } = useLayout((state) => state);\n  const { startChangeScrollPosition } = useHeaderStyle((state) => state);\n  const isHydrated = useHydrated();\n  const {\n    customHeaderBackgroundColor,\n    customHeaderChangeColorOnScroll,\n    headerBackgroundColor,\n    hideTabLinkWithLocation,\n    isShowListViewChangeButton,\n    isShowTabLink,\n    isHideSidebar,\n  } = useHeaderOptions();\n  const opacity = useTransform(\n    scrollY,\n    [0, startChangeScrollPosition, startChangeScrollPosition + 80],\n    [0, 0, customHeaderChangeColorOnScroll ? (startChangeScrollPosition ? 1 : 0) : 1],\n  );\n  const y = useTransform(\n    scrollY,\n    [0, startChangeScrollPosition, startChangeScrollPosition + 80],\n    [60, 60, customHeaderChangeColorOnScroll ? (startChangeScrollPosition ? 0 : 60) : 0],\n  );\n\n  return (\n    <div\n      className={headerStyles({\n        miniSidebar: sidebarMiniMode.value,\n        boxedSidebar: sidebarBoxedMode.value,\n        hideSidebar: isHideSidebar,\n      })}\n    >\n      <motion.div\n        className={backgroundColorStyles({\n          boxedSidebar: sidebarBoxedMode.value,\n          isShowTablink: isShowTabLink && !hideTabLinkWithLocation,\n          miniSidebar: sidebarMiniMode.value,\n          customBackgroundColor: customHeaderBackgroundColor,\n          hideSidebar: isHideSidebar,\n        })}\n        style={{\n          opacity: isHydrated ? opacity : 0,\n          backgroundColor: headerBackgroundColor,\n        }}\n      >\n        {customHeaderBackgroundColor ? (\n          <div className=\"pointer-events-none h-full w-full bg-background/[0.2]\" />\n        ) : null}\n      </motion.div>\n      <ControlNavigation />\n      <div className=\"flex flex-row items-center justify-end gap-x-2\">\n        {isShowListViewChangeButton ? (\n          <motion.div\n            style={{ opacity: isHydrated ? opacity : 0, y }}\n            transition={{ duration: 0.3 }}\n            className=\"flex flex-row items-center justify-end gap-x-3\"\n          >\n            {isShowListViewChangeButton ? <ListViewChangeButton /> : null}\n          </motion.div>\n        ) : null}\n        <MultiLevelDropdown user={user} />\n      </div>\n    </div>\n  );\n};\n\nexport default Header;\n"
  },
  {
    "path": "app/components/layouts/Layout.tsx",
    "content": "/* eslint-disable react-hooks/exhaustive-deps */\nimport { useEffect, useMemo, useRef, type CSSProperties } from 'react';\nimport { useMediaQuery } from '@react-hookz/web';\nimport {\n  useLocation,\n  useMatches,\n  useNavigationType,\n  useOutlet,\n  useParams,\n  type UIMatch,\n} from '@remix-run/react';\nimport type { User } from '@supabase/supabase-js';\nimport { AnimatePresence, motion, useMotionValueEvent, useScroll } from 'framer-motion';\nimport { useTheme } from 'next-themes';\nimport { Toaster } from 'sonner';\nimport { tv } from 'tailwind-variants';\n\nimport type { Handle } from '~/types/handle';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport { useHeaderStyle } from '~/store/layout/useHeaderStyle';\nimport { useHistoryStack } from '~/store/layout/useHistoryStack';\nimport { useLayout } from '~/store/layout/useLayout';\nimport {\n  ScrollArea,\n  ScrollBar,\n  ScrollCorner,\n  ScrollViewport,\n} from '~/components/elements/ScrollArea';\nimport TabLink from '~/components/elements/tab/TabLink';\n\nimport ActionButtons from './ActionButtons';\nimport BottomNav from './BottomNav';\nimport GlobalPlayer from './GlobalPlayer';\nimport Header from './Header';\nimport MobileHeader from './MobileHeader';\nimport SideBar from './SideBar';\nimport TailwindIndicator from './TailwindIndicator';\n\ninterface ILayout {\n  user?: User;\n}\n\nconst layoutStyles = tv({\n  base: 'flex max-h-full min-h-screen max-w-full flex-nowrap justify-start bg-content1/[0.3] transition-[padding] duration-200',\n  variants: {\n    boxed: {\n      true: 'min-h-[calc(100vh_-_115px)] py-[15px]',\n      false: ' p-0',\n    },\n  },\n  defaultVariants: {\n    boxed: false,\n  },\n});\n\nconst contentAreaStyles = tv({\n  base: 'ml-0 flex w-full grow flex-col justify-end overflow-hidden !rounded-none bg-background shadow-medium transition-[margin] duration-200 sm:border-divider',\n  variants: {\n    mini: {\n      true: 'sm:ml-[80px] sm:!rounded-tl-medium sm:border-l sm:border-t',\n    },\n    boxed: {\n      true: 'sm:ml-[280px] sm:!rounded-medium sm:border',\n    },\n    hideSidebar: {\n      true: 'sm:ml-0',\n    },\n  },\n  compoundVariants: [\n    {\n      mini: true,\n      boxed: true,\n      hideSidebar: false,\n      class: 'sm:ml-[110px] sm:!rounded-medium sm:border',\n    },\n    {\n      mini: false,\n      boxed: false,\n      hideSidebar: false,\n      class: 'sm:ml-[250px] sm:!rounded-tl-medium sm:border-l sm:border-t',\n    },\n    {\n      boxed: true,\n      hideSidebar: true,\n      class: 'sm:ml-[15px] sm:!rounded-medium sm:border',\n    },\n  ],\n  defaultVariants: {\n    mini: false,\n    boxed: false,\n    hideSidebar: false,\n  },\n});\n\nconst scrollAreaViewportStyles = tv({\n  base: 'flex w-[100vw] flex-col items-center justify-start transition-[width,_height] duration-200',\n  variants: {\n    mini: {\n      true: 'min-h-[calc(100vh_-_1px)] sm:w-[calc(100vw_-_80px)]',\n    },\n    boxed: {\n      true: 'min-h-[calc(100vh_-_32px)] sm:w-[calc(100vw_-_280px)]',\n    },\n    layoutPadding: {\n      true: 'mb-[70px] p-0 sm:px-5',\n      false: 'mb-[70px] p-0',\n    },\n    isShowTabLink: {\n      true: 'mt-[128px]',\n      false: 'mt-[72px]',\n    },\n    hideSidebar: {\n      true: 'min-h-[calc(100vh_-_1px)] sm:w-[100vw]',\n    },\n  },\n  compoundVariants: [\n    {\n      mini: true,\n      boxed: true,\n      hideSidebar: false,\n      class: 'min-h-[calc(100vh_-_32px)] sm:w-[calc(100vw_-_110px)]',\n    },\n    {\n      mini: false,\n      boxed: false,\n      hideSidebar: false,\n      class: 'min-h-[calc(100vh_-_1px)] sm:w-[calc(100vw_-_250px)]',\n    },\n    {\n      boxed: true,\n      hideSidebar: true,\n      class: 'min-h-[calc(100vh_-_32px)] sm:w-[calc(100vw_-_15px)]',\n    },\n    {\n      layoutPadding: false,\n      isShowTabLink: false,\n      hideSidebar: false,\n      class: 'mt-0',\n    },\n    {\n      layoutPadding: true,\n      isShowTabLink: false,\n      hideSidebar: false,\n      class: 'mt-[72px]',\n    },\n  ],\n  defaultVariants: {\n    mini: false,\n    boxed: false,\n    layoutPadding: true,\n    isShowTabLink: false,\n    hideSidebar: false,\n  },\n});\n\nconst tabLinkWrapperStyles = tv({\n  base: 'fixed z-[1000] flex h-[56px] w-[100vw] items-end shadow-md shadow-default/10',\n  variants: {\n    miniSidebar: {\n      true: 'top-[56px] sm:w-[calc(100vw_-_80px)]',\n    },\n    boxedSidebar: {\n      true: 'top-[71px] sm:w-[calc(100vw_-_280px)]',\n    },\n    hideSidebar: {\n      true: 'top-[56px] sm:w-[100vw]',\n    },\n  },\n  compoundVariants: [\n    {\n      miniSidebar: true,\n      boxedSidebar: true,\n      hideSidebar: false,\n      class: 'top-[79px] sm:w-[calc(100vw_-_110px)]',\n    },\n    {\n      miniSidebar: false,\n      boxedSidebar: false,\n      hideSidebar: false,\n      class: 'top-[56px] sm:w-[calc(100vw_-_250px)]',\n    },\n    {\n      boxedSidebar: true,\n      hideSidebar: true,\n      class: 'top-[79px] sm:w-[calc(100vw_-_15px)]',\n    },\n  ],\n  defaultVariants: {\n    miniSidebar: false,\n    boxedSidebar: false,\n    hideSidebar: false,\n  },\n});\n\nconst Layout = (props: ILayout) => {\n  const { user } = props;\n  const location = useLocation();\n  const matches = useMatches() as UIMatch<unknown, Handle>[];\n  const outlet = useOutlet();\n  const params = useParams();\n  const isHydrated = useHydrated();\n  const navigationType = useNavigationType();\n  const { theme } = useTheme();\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const isMd = useMediaQuery('(max-width: 1280px)', { initializeWithValue: false });\n  const { sidebarMiniMode, sidebarBoxedMode, sidebarHoverMode } = useSoraSettings();\n  const viewportRef = useRef<HTMLDivElement>(null);\n  const {\n    setViewportRef,\n    setScrollY,\n    setScrollYProgress,\n    scrollDirection,\n    setScrollDirection,\n    isShowOverlay,\n  } = useLayout((state) => state);\n  const { scrollY, scrollYProgress } = useScroll({ container: viewportRef });\n  useMotionValueEvent(scrollY, 'change', (latest) => {\n    const lastScrollY = scrollY.getPrevious();\n    if (isSm) {\n      const direction = latest > lastScrollY ? 'down' : 'up';\n      if (\n        direction !== scrollDirection &&\n        (latest - lastScrollY > 20 || latest - lastScrollY < -20)\n      ) {\n        setScrollDirection(direction);\n      }\n    }\n  });\n  const { historyBack, historyForward, setHistoryBack, setHistoryForward } = useHistoryStack(\n    (state) => state,\n  );\n  const {\n    backgroundColor,\n    setBackgroundColor,\n    setStartChangeScrollPosition,\n    startChangeScrollPosition,\n  } = useHeaderStyle((headerState) => headerState);\n  const isShowTabLink = useMemo(\n    () => matches.some((match) => match?.handle?.showTabLink === true),\n    [matches],\n  );\n  const disableLayoutPadding = useMemo(\n    () => matches.some((match) => match?.handle?.disableLayoutPadding === true),\n    [matches],\n  );\n  const isHideSidebar = useMemo(\n    () => matches.some((match) => match?.handle?.hideSidebar === true),\n    [matches],\n  );\n  const currentTabLinkPages = useMemo(() => {\n    const currentMatch = matches.find((match) => match?.handle?.showTabLink);\n    if (typeof currentMatch?.handle?.tabLinkPages === 'function')\n      // @ts-ignore\n      return currentMatch?.handle?.tabLinkPages?.({ params });\n    return currentMatch?.handle?.tabLinkPages;\n  }, [matches]);\n  const currentTabLinkTo = useMemo(() => {\n    const currentMatch = matches.find((match) => match?.handle?.showTabLink);\n    if (typeof currentMatch?.handle?.tabLinkTo === 'function')\n      return currentMatch?.handle?.tabLinkTo?.({ params });\n    return undefined;\n  }, [matches]);\n  const customHeaderBackgroundColor = useMemo(\n    () => matches.some((match) => match?.handle?.customHeaderBackgroundColor === true),\n    [matches],\n  );\n  const customHeaderChangeColorOnScroll = useMemo(\n    () => matches.some((match) => match?.handle?.customHeaderChangeColorOnScroll === true),\n    [matches],\n  );\n  const hideTabLinkWithLocation: boolean = useMemo(() => {\n    const currentMatch = matches.find((match) => match?.handle?.showTabLink);\n    if (currentMatch?.handle?.hideTabLinkWithLocation)\n      return currentMatch?.handle?.hideTabLinkWithLocation?.(location.pathname) ?? true;\n    return false;\n  }, [matches, location.pathname]);\n\n  useEffect(() => {\n    setHistoryBack([location.key]);\n    setHistoryForward([location.key]);\n    setViewportRef(viewportRef);\n    setScrollY(scrollY);\n    setScrollYProgress(scrollYProgress);\n  }, []);\n\n  useEffect(() => {\n    const preventScrollToTopRoute = matches.some(\n      (match) => match.handle && (match.handle as Handle).preventScrollToTop === true,\n    );\n    if (!preventScrollToTopRoute) {\n      viewportRef.current?.scrollTo(0, 0);\n    }\n    if (!customHeaderBackgroundColor && backgroundColor !== '') {\n      setBackgroundColor('');\n    }\n    if (!customHeaderChangeColorOnScroll && startChangeScrollPosition !== 0) {\n      setStartChangeScrollPosition(0);\n    }\n  }, [location]);\n\n  useEffect(() => {\n    if (navigationType === 'PUSH') {\n      setHistoryBack([...historyBack, location.key]);\n      setHistoryForward([location.key]);\n    } else if (navigationType === 'POP') {\n      // detect if user is going back or forward\n      if (historyBack.length > 0 && historyBack[historyBack.length - 2] === location.key) {\n        // going back\n        setHistoryBack([...historyBack.slice(0, historyBack.length - 1)]);\n        setHistoryForward([...historyForward, location.key]);\n      } else if (\n        historyForward.length > 0 &&\n        historyForward[historyForward.length - 2] === location.key\n      ) {\n        // going forward\n        setHistoryForward([...historyForward.slice(0, historyForward.length - 1)]);\n        setHistoryBack([...historyBack, location.key]);\n      }\n    } else if (navigationType === 'REPLACE') {\n      setHistoryBack([...historyBack.slice(0, historyBack.length - 1), location.key]);\n      setHistoryForward([...historyForward.slice(0, historyForward.length - 1)]);\n    }\n  }, [location.key, navigationType]);\n\n  useEffect(() => {\n    if (isMd && !sidebarMiniMode.value) {\n      sidebarMiniMode.set(true);\n      if (!sidebarHoverMode.value) sidebarHoverMode.set(false);\n    }\n    if (isSm && sidebarBoxedMode.value === true) {\n      sidebarBoxedMode.set(false);\n    }\n  }, [isMd, isSm, sidebarMiniMode.value, sidebarHoverMode.value, sidebarBoxedMode.value]);\n\n  return (\n    <div className={layoutStyles({ boxed: sidebarBoxedMode.value })}>\n      {isSm || isHideSidebar ? null : <SideBar />}\n      {isShowOverlay ? (\n        <motion.div\n          initial={{ opacity: 0 }}\n          animate={{ opacity: 1 }}\n          transition={{ duration: 0.3, ease: 'easeInOut' }}\n          className=\"fixed inset-0 z-[9998] bg-[rgba(0,_0,_0,_.90)]\"\n        />\n      ) : null}\n      <div\n        className={contentAreaStyles({\n          mini: sidebarMiniMode.value,\n          boxed: sidebarBoxedMode.value,\n          hideSidebar: isHideSidebar,\n        })}\n      >\n        {isSm ? <MobileHeader /> : <Header user={user} />}\n        {<TailwindIndicator />}\n        {isShowTabLink && !hideTabLinkWithLocation ? (\n          <div\n            className={tabLinkWrapperStyles({\n              miniSidebar: sidebarMiniMode.value,\n              boxedSidebar: sidebarBoxedMode.value,\n              hideSidebar: isHideSidebar,\n            })}\n          >\n            <TabLink pages={currentTabLinkPages} linkTo={currentTabLinkTo} />\n          </div>\n        ) : null}\n        {isHydrated ? <ActionButtons /> : null}\n        <ScrollArea\n          type={isSm ? 'scroll' : 'always'}\n          scrollHideDelay={500}\n          className={`w-full ${\n            sidebarBoxedMode.value ? 'h-[calc(100vh-32px)]' : 'h-[calc(100vh-1px)]'\n          }`}\n          key=\"scroll-area-main\"\n        >\n          <ScrollViewport ref={viewportRef} data-restore-scroll=\"true\">\n            <main\n              className={scrollAreaViewportStyles({\n                mini: sidebarMiniMode.value,\n                boxed: sidebarBoxedMode.value,\n                layoutPadding: !disableLayoutPadding,\n                isShowTabLink: isShowTabLink && !hideTabLinkWithLocation,\n                hideSidebar: isHideSidebar,\n              })}\n            >\n              <GlobalPlayer />\n              <Toaster\n                // @ts-ignore\n                theme={theme}\n                position=\"bottom-right\"\n                richColors\n                closeButton\n                toastOptions={{\n                  style: {\n                    '--normal-bg': 'hsl(var(--theme-default))',\n                    '--normal-text': 'hsl(var(--theme-default-foreground))',\n                    '--normal-border': 'hsl(var(--theme-border))',\n                    '--success-bg': 'hsl(var(--theme-success))',\n                    '--success-border': 'hsl(var(--theme-border))',\n                    '--success-text': 'hsl(var(--theme-success-foreground))',\n                    '--error-bg': 'hsl(var(--theme-danger))',\n                    '--error-border': 'hsl(var(--theme-border))',\n                    '--error-text': 'hsl(var(--theme-danger-foreground))',\n                    '--gray1': 'hsl(var(--theme-default-50))',\n                    '--gray2': 'hsl(var(--theme-default-100))',\n                    '--gray4': 'hsl(var(--theme-default-300))',\n                    '--gray5': 'hsl(var(--theme-default-400))',\n                    '--gray12': 'hsl(var(--theme-default-900))',\n                  } as CSSProperties,\n                }}\n              />\n              <AnimatePresence mode=\"wait\" initial={false}>\n                {outlet}\n              </AnimatePresence>\n            </main>\n            {isSm ? <BottomNav user={user} /> : null}\n          </ScrollViewport>\n          <ScrollBar />\n          <ScrollCorner />\n        </ScrollArea>\n      </div>\n    </div>\n  );\n};\n\nexport default Layout;\n"
  },
  {
    "path": "app/components/layouts/MobileHeader.tsx",
    "content": "import { Button } from '@nextui-org/button';\nimport { NavLink, useLocation, useNavigate } from '@remix-run/react';\nimport { motion, useTransform } from 'framer-motion';\n\nimport { useHeaderOptions } from '~/utils/react/hooks/useHeader';\nimport { useHeaderStyle } from '~/store/layout/useHeaderStyle';\nimport { useHistoryStack } from '~/store/layout/useHistoryStack';\nimport { useLayout } from '~/store/layout/useLayout';\nimport Arrow from '~/assets/icons/ArrowIcon';\nimport Search from '~/assets/icons/SearchIcon';\n\nconst MobileHeader = () => {\n  const location = useLocation();\n  const navigate = useNavigate();\n  const { historyBack } = useHistoryStack((state) => state);\n  const { scrollY } = useLayout((state) => state);\n  const { startChangeScrollPosition } = useHeaderStyle((state) => state);\n  const {\n    isShowMobileHeader,\n    isShowTabLink,\n    hideTabLinkWithLocation,\n    customHeaderBackgroundColor,\n    currentMiniTitle,\n    headerBackgroundColor,\n    customHeaderChangeColorOnScroll,\n  } = useHeaderOptions();\n  const opacity = useTransform(\n    scrollY,\n    [0, startChangeScrollPosition, startChangeScrollPosition + 80],\n    [0, 0, customHeaderChangeColorOnScroll ? (startChangeScrollPosition ? 1 : 0) : 1],\n  );\n  const y = useTransform(\n    scrollY,\n    [0, startChangeScrollPosition, startChangeScrollPosition + 80],\n    [60, 60, customHeaderChangeColorOnScroll ? (startChangeScrollPosition ? 0 : 60) : 0],\n  );\n  const handleBackButton = () => {\n    if (historyBack.length > 1) {\n      navigate(-1);\n    } else {\n      navigate('/');\n    }\n  };\n  const { scrollDirection } = useLayout((state) => state);\n\n  if (!isShowMobileHeader) {\n    return null;\n  }\n  if (location.pathname === '/') {\n    return (\n      <motion.div\n        initial={{ y: 0 }}\n        animate={{ y: scrollDirection === 'down' ? -65 : 0 }}\n        transition={{ duration: 0.5 }}\n        className=\"fixed top-0 z-[1000] flex h-[64px] w-full flex-row items-center justify-between bg-background/60 px-6 shadow-lg backdrop-blur-2xl backdrop-contrast-125 backdrop-saturate-200 sm:hidden\"\n      >\n        <NavLink\n          to=\"/\"\n          arial-label=\"home-page\"\n          className=\"bg-gradient-to-tr from-secondary to-primary to-50% bg-clip-text text-3xl font-bold tracking-normal text-transparent md:text-4xl\"\n        >\n          SORA\n        </NavLink>\n        <Button\n          variant=\"light\"\n          color=\"default\"\n          isIconOnly\n          onPress={() => navigate('/search/movie')}\n        >\n          <Search filled />\n        </Button>\n      </motion.div>\n    );\n  }\n  return (\n    <div className=\"fixed top-0 z-[1000] flex h-[64px] w-[100vw] flex-row items-center justify-start gap-x-3 px-3 py-2 shadow-none sm:hidden\">\n      <motion.div\n        className=\"absolute left-0 top-0 z-[-1] w-full backdrop-blur-2xl backdrop-contrast-125 backdrop-saturate-200\"\n        style={{\n          opacity,\n          backgroundColor: headerBackgroundColor,\n          height: isShowTabLink && !hideTabLinkWithLocation ? 112 : 64,\n        }}\n      >\n        {customHeaderBackgroundColor ? (\n          <div className=\"pointer-events-none h-full w-full bg-background/[0.2]\" />\n        ) : null}\n      </motion.div>\n      <Button variant=\"faded\" radius=\"full\" isIconOnly onPress={() => handleBackButton()}>\n        <Arrow direction=\"left\" />\n      </Button>\n      <div className=\"flex flex-row items-center justify-between\">\n        {currentMiniTitle ? (\n          <motion.span\n            style={{ opacity, y }}\n            transition={{ duration: 0.3 }}\n            className=\"flex flex-col items-start justify-center\"\n          >\n            <div className=\"flex flex-col items-start justify-center\">\n              <span className=\"line-clamp-1 text-xl font-semibold\">{currentMiniTitle.title}</span>\n              {currentMiniTitle.subtitle ? (\n                <span className=\"line-clamp-1 text-xs font-medium opacity-75\">\n                  {currentMiniTitle.subtitle}\n                </span>\n              ) : null}\n            </div>\n          </motion.span>\n        ) : null}\n      </div>\n    </div>\n  );\n};\n\nexport default MobileHeader;\n"
  },
  {
    "path": "app/components/layouts/MultiLevelDropdown.tsx",
    "content": "import { useEffect, useMemo, useState } from 'react';\nimport { Player } from '@lottiefiles/react-lottie-player';\nimport { Avatar } from '@nextui-org/avatar';\nimport { Button } from '@nextui-org/button';\nimport { Divider } from '@nextui-org/divider';\nimport { Spacer } from '@nextui-org/spacer';\nimport { useLocation, useNavigate, useSearchParams } from '@remix-run/react';\nimport type { User } from '@supabase/supabase-js';\nimport type { AnimationItem } from 'lottie-web';\nimport { useTheme } from 'next-themes';\nimport { useTranslation } from 'react-i18next';\n\nimport { getBackgroundTitleBarColor, setMetaThemeColor } from '~/utils/client/meta-tags.client';\nimport useColorDarkenLighten from '~/utils/react/hooks/useColorDarkenLighten';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport languages from '~/constants/languages';\nimport { listCustomThemeColors, listDefaultThemeColors } from '~/constants/settings';\nimport { Popover, PopoverContent, PopoverTrigger } from '~/components/elements/Popover';\nimport ResizablePanel from '~/components/elements/shared/ResizablePanel';\nimport Arrow from '~/assets/icons/ArrowIcon';\nimport Brush from '~/assets/icons/BrushIcon';\nimport GlobalIcon from '~/assets/icons/GlobalIcon';\nimport Tick from '~/assets/icons/TickIcon';\nimport avatar from '~/assets/images/avatar.png';\nimport dropdown from '~/assets/lotties/lottieflow-dropdown-03-0072F5-easey.json';\n\ninterface IMultiLevelDropdownProps {\n  user?: User | undefined;\n}\n\nconst MultiLevelDropdown = (props: IMultiLevelDropdownProps) => {\n  const { user } = props;\n  const { t } = useTranslation('header');\n  const rootData = useTypedRouteLoaderData('root');\n  const { locale } = rootData || { locale: 'en' };\n  const { setTheme, theme: currentTheme } = useTheme();\n  const navigate = useNavigate();\n  const location = useLocation();\n  const isHydrated = useHydrated();\n  const [search] = useSearchParams();\n  const [currentLevel, setCurrentLevel] = useState('general');\n  const [isDropdownOpen, setIsDropdownOpen] = useState(false);\n  const { isLightDarkThemeOnly, currentThemeColor } = useSoraSettings();\n  const { isDark } = useColorDarkenLighten();\n  const [lottie, setLottie] = useState<AnimationItem>();\n  useEffect(() => {\n    if (isDropdownOpen) {\n      lottie?.playSegments([0, 50], true);\n    } else {\n      lottie?.playSegments([50, 96], true);\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [isDropdownOpen]);\n\n  const ref = useMemo(\n    () =>\n      (search.get('ref') || location.pathname + location.search)\n        .replace('?', '_0x3F_')\n        .replace('&', '_0x26'),\n    [location.pathname, location.search, search],\n  );\n  const parts = user?.email?.split('@');\n  const username = parts?.shift();\n\n  const dropdownLevel = useMemo(() => {\n    return [\n      {\n        id: 'general',\n        key: 'general',\n        showTitle: true,\n        showAvatar: true,\n        showBackButton: false,\n        backButtonAction: () => null,\n        title: user ? username : t('sign-in'),\n        isTitleClickable: true,\n        titleAction: () => (user ? null : navigate(`/sign-in?ref=${ref}`)),\n        listItems: [\n          {\n            id: 'language',\n            title: t('language'),\n            description: 'Change the language of the website',\n            showIcon: true,\n            icon: <GlobalIcon />,\n            action: () => setCurrentLevel('language'),\n            currentValue: locale,\n          },\n          {\n            id: 'display',\n            title: t('display'),\n            description: 'Change the display of the website',\n            showIcon: true,\n            icon: <Brush />,\n            action: () => setCurrentLevel('display'),\n          },\n          {\n            id: 'sign-up-log-out',\n            title: user ? t('log-out') : t('sign-up'),\n            description: user ? 'Log out of your account' : 'Sign up for an account',\n            showIcon: false,\n            icon: null,\n            action: () => navigate(user ? `/sign-out?ref=${ref}` : `/sign-up?ref=${ref}`),\n          },\n        ],\n      },\n      {\n        id: 'language',\n        key: 'language',\n        showTitle: true,\n        showAvatar: false,\n        showBackButton: true,\n        backButtonAction: () => setCurrentLevel('general'),\n        title: t('language'),\n        isTitleClickable: false,\n        titleAction: () => null,\n        listItems: languages.map((language) => ({\n          id: language,\n          title: t(language),\n          showIcon: false,\n          icon: null,\n          action: () => navigate(`${location.pathname}?lng=${language}`),\n          currentValue: null,\n          isCurrent: locale === language,\n        })),\n      },\n      {\n        id: 'display',\n        key: 'display',\n        showTitle: true,\n        showAvatar: false,\n        showBackButton: true,\n        backButtonAction: () => setCurrentLevel('general'),\n        title: t('display'),\n        isTitleClickable: false,\n        titleAction: () => null,\n        listItems:\n          isLightDarkThemeOnly.value === true\n            ? [\n                {\n                  id: 'theme',\n                  title: t('theme'),\n                  description: 'Change the theme of the website',\n                  showIcon: true,\n                  icon: <Brush />,\n                  action: () => setCurrentLevel('theme'),\n                  currentValue:\n                    currentTheme === 'system' ? t('system') : isDark ? t('dark') : t('light'),\n                },\n                {\n                  id: 'theme-color',\n                  title: t('theme-color'),\n                  description: 'Change the colors of the theme',\n                  showIcon: true,\n                  icon: <Brush />,\n                  action: () => setCurrentLevel('theme-color'),\n                  currentValue: t(currentThemeColor.value || 'blue'),\n                },\n              ]\n            : listCustomThemeColors.map((theme) => ({\n                id: theme,\n                title: t(theme),\n                description: theme,\n                showIcon: false,\n                icon: null,\n                action: async () => {\n                  await setTheme(theme);\n                  const color = await getBackgroundTitleBarColor(isHydrated);\n                  await setMetaThemeColor(`hsl(${color})`);\n                },\n                currentValue: null,\n                isCurrent: currentTheme === theme,\n              })),\n      },\n      {\n        id: 'theme',\n        key: 'theme',\n        showTitle: true,\n        showAvatar: false,\n        showBackButton: true,\n        backButtonAction: () => setCurrentLevel('display'),\n        title: t('theme'),\n        isTitleClickable: false,\n        titleAction: () => null,\n        listItems: [\n          {\n            id: 'dark',\n            title: t('dark'),\n            showIcon: false,\n            icon: null,\n            action: async () => {\n              if (currentThemeColor.value !== 'blue') {\n                await setTheme(`dark-${currentThemeColor.value}`);\n              } else {\n                await setTheme('dark');\n              }\n              const color = await getBackgroundTitleBarColor(isHydrated);\n              await setMetaThemeColor(`hsl(${color})`);\n            },\n            currentValue: null,\n            isCurrent: isDark && currentTheme !== 'system',\n          },\n          {\n            id: 'light',\n            title: t('light'),\n            showIcon: false,\n            icon: null,\n            action: async () => {\n              if (currentThemeColor.value !== 'blue') {\n                await setTheme(`light-${currentThemeColor.value}`);\n              } else {\n                await setTheme('light');\n              }\n              const color = await getBackgroundTitleBarColor(isHydrated);\n              await setMetaThemeColor(`hsl(${color})`);\n            },\n            currentValue: null,\n            isCurrent: !isDark && currentTheme !== 'system',\n          },\n          {\n            id: 'system',\n            title: t('system'),\n            showIcon: false,\n            icon: null,\n            action: async () => {\n              await currentThemeColor.set('blue');\n              await setTheme('system');\n              const color = await getBackgroundTitleBarColor(isHydrated);\n              await setMetaThemeColor(`hsl(${color})`);\n            },\n            currentValue: null,\n            isCurrent: currentTheme === 'system',\n          },\n        ],\n      },\n      {\n        id: 'theme-color',\n        key: 'theme-color',\n        showTitle: true,\n        showAvatar: false,\n        showBackButton: true,\n        backButtonAction: () => setCurrentLevel('display'),\n        title: t('theme-color'),\n        isTitleClickable: false,\n        titleAction: () => null,\n        listItems: listDefaultThemeColors.map((theme) => ({\n          id: theme,\n          title: t(theme),\n          showIcon: false,\n          icon: null,\n          action: async () => {\n            await currentThemeColor.set(theme);\n            if (isDark) {\n              if (theme !== 'blue') {\n                await setTheme(`dark-${theme}`);\n              } else {\n                await setTheme('dark');\n              }\n            } else {\n              if (theme !== 'blue') {\n                await setTheme(`light-${theme}`);\n              } else {\n                await setTheme('light');\n              }\n            }\n            const color = await getBackgroundTitleBarColor(isHydrated);\n            await setMetaThemeColor(`hsl(${color})`);\n          },\n          currentValue: null,\n          isCurrent: currentThemeColor.value ? currentThemeColor.value === theme : theme === 'blue',\n        })),\n      },\n    ];\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [\n    ref,\n    currentTheme,\n    locale,\n    user,\n    username,\n    t,\n    currentThemeColor.value,\n    isDark,\n    isHydrated,\n    isLightDarkThemeOnly.value,\n  ]);\n\n  const currentDropdownLevel = useMemo(\n    () => dropdownLevel.find((level) => level.id === currentLevel),\n    [dropdownLevel, currentLevel],\n  );\n\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  const settingsOptions = (item: any) => (\n    <Button\n      key={item.id}\n      type=\"button\"\n      fullWidth\n      variant=\"light\"\n      onPress={item.action}\n      className=\"flex h-14 flex-row items-center justify-between gap-x-8 !p-2 data-[hover=true]:bg-default/[.6]\"\n    >\n      <div className=\"flex shrink-0 grow flex-row items-center gap-x-2\">\n        {item?.showIcon ? (\n          (\n            item as {\n              id: string;\n              title: string;\n              description: string;\n              showIcon: boolean;\n              icon: JSX.Element;\n              action: () => void;\n              currentValue: string;\n            }\n          )?.icon\n        ) : (\n            item as {\n              id: string;\n              title: string;\n              showIcon: boolean;\n              action: () => void;\n              isCurrent: boolean;\n            }\n          )?.isCurrent ? (\n          <Tick />\n        ) : (\n          <Spacer x={6} />\n        )}\n        <h6 className=\"!line-clamp-1 !text-default-foreground\">{item.title}</h6>\n      </div>\n      <div className=\"flex shrink-0 grow flex-row items-center justify-end gap-x-2\">\n        <p className=\"!text-default-foreground/80\">\n          {(\n            item as {\n              id: string;\n              title: string;\n              description: string;\n              showIcon: boolean;\n              icon: JSX.Element;\n              action: () => void;\n              currentValue: string;\n            }\n          )?.currentValue || ''}\n        </p>\n        {item.showIcon ? <Arrow direction=\"right\" /> : null}\n      </div>\n    </Button>\n  );\n\n  const handleOpenChange = (open: boolean) => {\n    setIsDropdownOpen(open);\n    if (!open) setCurrentLevel('general');\n  };\n\n  return (\n    <Popover open={isDropdownOpen} onOpenChange={(open) => handleOpenChange(open)}>\n      <PopoverTrigger asChild>\n        <Button\n          type=\"button\"\n          variant=\"faded\"\n          radius=\"full\"\n          aria-label=\"dropdown\"\n          isIconOnly\n          className=\"h-9 w-9\"\n          size=\"sm\"\n        >\n          <Player\n            lottieRef={(instance) => {\n              setLottie(instance);\n            }}\n            src={dropdown}\n            autoplay={false}\n            keepLastFrame\n            speed={2.7}\n            className=\"lottie-color h-6 w-6\"\n          />\n        </Button>\n      </PopoverTrigger>\n      <PopoverContent\n        side=\"bottom\"\n        align=\"end\"\n        alignOffset={-8}\n        className=\"z-[1000] bg-default/60 backdrop-blur-2xl backdrop-contrast-125 backdrop-saturate-200\"\n      >\n        <ResizablePanel contentWidth=\"fit\">\n          {currentDropdownLevel ? (\n            <div className=\"flex w-max flex-col items-start justify-start gap-y-2\">\n              {currentDropdownLevel?.showBackButton ||\n              currentDropdownLevel?.showAvatar ||\n              currentDropdownLevel?.showTitle ? (\n                <>\n                  <Button\n                    type=\"button\"\n                    isIconOnly\n                    fullWidth\n                    variant=\"light\"\n                    onPress={\n                      currentDropdownLevel?.isTitleClickable\n                        ? currentDropdownLevel?.titleAction\n                        : currentDropdownLevel?.backButtonAction\n                    }\n                    className=\"flex h-14 w-full flex-row items-center justify-between gap-x-2 p-2 data-[hover=true]:bg-default/[.6]\"\n                  >\n                    {currentDropdownLevel?.showBackButton ? <Arrow direction=\"left\" /> : null}\n                    {currentDropdownLevel?.showAvatar ? (\n                      <Avatar\n                        size=\"sm\"\n                        alt=\"Avatar\"\n                        src={avatar}\n                        color=\"primary\"\n                        radius=\"full\"\n                        isBordered\n                      />\n                    ) : null}\n                    {currentDropdownLevel?.showTitle ? (\n                      <h6 className=\"px-3 !text-default-foreground\">\n                        {currentDropdownLevel?.title}\n                      </h6>\n                    ) : null}\n                    <div />\n                  </Button>\n                  <Divider />\n                </>\n              ) : null}\n              <div className=\"flex w-full flex-col items-start justify-start gap-y-2\">\n                {currentDropdownLevel?.listItems.map((item) => settingsOptions(item))}\n              </div>\n            </div>\n          ) : null}\n        </ResizablePanel>\n      </PopoverContent>\n    </Popover>\n  );\n};\n\nexport default MultiLevelDropdown;\n"
  },
  {
    "path": "app/components/layouts/SideBar.tsx",
    "content": "import { Button } from '@nextui-org/button';\nimport { Card, CardBody, CardFooter } from '@nextui-org/card';\nimport { Link } from '@nextui-org/link';\nimport { Spinner } from '@nextui-org/spinner';\nimport { Tooltip } from '@nextui-org/tooltip';\nimport { useHover } from '@react-aria/interactions';\nimport { NavLink, useNavigate } from '@remix-run/react';\nimport { useTranslation } from 'react-i18next';\nimport { MimeType } from 'remix-image';\nimport { tv } from 'tailwind-variants';\n\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport Image from '~/components/elements/Image';\nimport {\n  NavigationMenu,\n  NavigationMenuContent,\n  NavigationMenuItem,\n  NavigationMenuLink,\n  NavigationMenuList,\n  NavigationMenuTrigger,\n  navigationMenuTriggerStyle,\n} from '~/components/elements/NavigationMenu';\nimport Anime from '~/assets/icons/AnimeIcon';\nimport CategoryIcon from '~/assets/icons/CategoryIcon';\nimport Discover from '~/assets/icons/DiscoverIcon';\nimport History from '~/assets/icons/HistoryIcon';\nimport Home from '~/assets/icons/HomeIcon';\nimport Menu from '~/assets/icons/MenuIcon';\nimport Movie from '~/assets/icons/MovieIcon';\nimport Search from '~/assets/icons/SearchIcon';\nimport Settings from '~/assets/icons/SettingsIcon';\nimport TrendingUp from '~/assets/icons/TrendingUpIcon';\nimport Tv from '~/assets/icons/TvIcon';\nimport TwoUsers from '~/assets/icons/TwoUsersIcon';\nimport Logo from '~/assets/images/logo_loading.png';\n\nconst sidebarStyles = tv({\n  base: 'fixed z-[1999] box-border hidden shrink-0 grow-0 transition-[max-width] duration-400 sm:block',\n  variants: {\n    sidebarMiniMode: {\n      true: 'w-full max-w-[80px] basis-[80px]',\n      false: 'w-full max-w-[250px] basis-[250px]',\n    },\n    sidebarBoxedMode: {\n      true: 'left-[15px] top-[15px] h-[calc(100vh_-_30px)] rounded-large border border-divider bg-background shadow-medium',\n      false: 'left-0 top-0 h-screen',\n    },\n    sidebarHoverMode: {\n      true: 'w-full max-w-[250px] basis-[250px] rounded-r-large border border-divider bg-background shadow-2xl',\n    },\n  },\n  compoundVariants: [{}],\n  defaultVariants: {\n    sidebarMiniMode: false,\n    sidebarBoxedMode: false,\n  },\n});\n\nconst sidebarActiveStyles = tv({\n  base: 'h-[56px] justify-start transition-[width] duration-400',\n  variants: {\n    sidebarMiniMode: {\n      true: 'w-[56px]',\n      false: 'w-[215px]',\n    },\n    sidebarHoverMode: {\n      true: 'w-[215px]',\n    },\n    sidebarRoundedAll: {\n      true: 'rounded-small',\n      false: 'rounded-r-small',\n    },\n    sidebarPillAll: {\n      true: 'rounded-[56px]',\n      false: 'rounded-r-[56px]',\n    },\n  },\n  defaultVariants: {\n    sidebarMiniMode: false,\n    sidebarRoundedAll: true,\n  },\n});\n\nconst viewportPositionStyles = tv({\n  base: '!fixed',\n  variants: {\n    sidebarMiniMode: {\n      true: '!left-[85px]',\n    },\n    sidebarHoverMode: {\n      true: '!left-[250px]',\n    },\n    sidebarBoxedMode: {\n      true: '!left-[265px]',\n    },\n  },\n  compoundVariants: [\n    {\n      sidebarMiniMode: true,\n      sidebarHoverMode: true,\n      sidebarBoxedMode: false,\n      class: '!left-[250px]',\n    },\n    {\n      sidebarMiniMode: true,\n      sidebarHoverMode: false,\n      sidebarBoxedMode: true,\n      class: '!left-[100px]',\n    },\n    {\n      sidebarMiniMode: false,\n      sidebarHoverMode: false,\n      sidebarBoxedMode: false,\n      class: '!left-[250px]',\n    },\n  ],\n  defaultVariants: {\n    sidebarMiniMode: false,\n    sidebarHoverMode: false,\n    sidebarBoxedMode: false,\n  },\n});\n\nconst navigationPartStyles = tv({\n  base: 'w-full overflow-x-visible overflow-y-scroll scrollbar-hide',\n  variants: {\n    sidebarBoxedMode: {\n      true: 'h-[calc(100%_-_100px)]',\n      false: 'h-[calc(100%_-_80px)]',\n    },\n  },\n  defaultVariants: {\n    sidebarBoxedMode: false,\n  },\n});\n\nconst SideBar = () => {\n  const { t } = useTranslation('sidebar');\n  const navigate = useNavigate();\n  const { sidebarMiniMode, sidebarHoverMode, sidebarBoxedMode } = useSoraSettings();\n  const { hoverProps: sidebarHoverProps, isHovered } = useHover({\n    isDisabled: !sidebarHoverMode.value,\n  });\n  const navigationItemWidthStyle = sidebarMiniMode.value && !isHovered ? 'w-[56px]' : 'w-[215px]';\n\n  return (\n    <aside\n      {...sidebarHoverProps}\n      className={sidebarStyles({\n        sidebarMiniMode: sidebarMiniMode.value,\n        sidebarBoxedMode: sidebarBoxedMode.value,\n        sidebarHoverMode: isHovered,\n      })}\n    >\n      <div className=\"mb-3 ml-4 flex h-[65px] w-full flex-row items-center justify-start\">\n        <Button\n          className={`${\n            sidebarMiniMode.value && !isHovered ? 'basis-[50px]' : 'basis-[60px]'\n          } flex shrink-0 grow-0 justify-center`}\n          isIconOnly\n          variant=\"light\"\n          onPress={() => {\n            sidebarMiniMode.set(!sidebarMiniMode.value);\n          }}\n        >\n          <Menu />\n        </Button>\n        {sidebarMiniMode.value && !isHovered ? null : (\n          <div className=\"flex items-center gap-x-3\">\n            <Image\n              width=\"30px\"\n              height=\"30px\"\n              radius=\"full\"\n              alt=\"Logo\"\n              src={Logo}\n              placeholder=\"empty\"\n              responsive={[\n                {\n                  size: {\n                    width: 30,\n                    height: 30,\n                  },\n                },\n              ]}\n              loaderUrl=\"/api/image\"\n              dprVariants={[1, 3]}\n              options={{\n                contentType: MimeType.WEBP,\n              }}\n            />\n            <NavLink\n              to=\"/\"\n              arial-label=\"home-page\"\n              className=\"bg-gradient-to-tr from-secondary to-primary to-50% bg-clip-text text-3xl font-bold tracking-normal text-transparent focus:outline-none focus:ring-2 focus:ring-focus md:text-4xl\"\n            >\n              SORA\n            </NavLink>\n          </div>\n        )}\n      </div>\n      <div\n        className={navigationPartStyles({\n          sidebarBoxedMode: sidebarBoxedMode.value,\n        })}\n      >\n        <NavigationMenu\n          orientation=\"vertical\"\n          viewportPositionClassName={viewportPositionStyles({\n            sidebarMiniMode: sidebarMiniMode.value,\n            sidebarHoverMode: sidebarHoverMode.value,\n            sidebarBoxedMode: sidebarBoxedMode.value,\n          })}\n        >\n          <NavigationMenuList className=\"m-0 gap-3 [&_.active]:bg-default [&_.active]:text-default-foreground\">\n            <NavigationMenuItem\n              className={`${navigationItemWidthStyle} text-left transition-[width] duration-200`}\n              value=\"home\"\n            >\n              <Tooltip\n                content={t('home')}\n                isDisabled={!sidebarMiniMode.value || (sidebarHoverMode && isHovered)}\n                placement=\"right\"\n                offset={10}\n                showArrow\n              >\n                <NavigationMenuLink asChild>\n                  <NavLink\n                    to=\"/\"\n                    className={navigationMenuTriggerStyle({\n                      class: `${navigationItemWidthStyle} h-[56px] justify-start transition-[width] duration-200`,\n                    })}\n                  >\n                    {({ isActive, isPending }) => (\n                      <>\n                        <Home\n                          className={\n                            !sidebarMiniMode.value || (sidebarHoverMode && isHovered) ? 'mr-4' : ''\n                          }\n                          filled={isActive}\n                        />\n                        {!sidebarMiniMode.value || (sidebarHoverMode && isHovered)\n                          ? t('home')\n                          : null}\n                        <Spinner\n                          size=\"sm\"\n                          classNames={{\n                            base:\n                              isPending &&\n                              (!sidebarMiniMode.value || (sidebarHoverMode && isHovered))\n                                ? 'ml-auto'\n                                : '!hidden',\n                            circle1: 'border-b-default-foreground',\n                            circle2: 'border-b-default-foreground',\n                          }}\n                        />\n                      </>\n                    )}\n                  </NavLink>\n                </NavigationMenuLink>\n              </Tooltip>\n            </NavigationMenuItem>\n            <NavigationMenuItem\n              className={`${navigationItemWidthStyle} text-left transition-[width] duration-200`}\n              value=\"trending\"\n            >\n              <Tooltip\n                content={t('trending')}\n                isDisabled={!sidebarMiniMode.value || (sidebarHoverMode && isHovered)}\n                placement=\"right\"\n                showArrow\n                offset={10}\n              >\n                <NavigationMenuLink asChild>\n                  <NavLink\n                    to=\"/trending/\"\n                    className={navigationMenuTriggerStyle({\n                      class: `${navigationItemWidthStyle} h-[56px] justify-start transition-[width] duration-200`,\n                    })}\n                  >\n                    {({ isActive, isPending }) => (\n                      <>\n                        <TrendingUp\n                          className={\n                            !sidebarMiniMode.value || (sidebarHoverMode && isHovered) ? 'mr-4' : ''\n                          }\n                          filled={isActive}\n                        />\n                        {!sidebarMiniMode.value || (sidebarHoverMode && isHovered)\n                          ? t('trending')\n                          : null}\n                        <Spinner\n                          size=\"sm\"\n                          classNames={{\n                            base:\n                              isPending &&\n                              (!sidebarMiniMode.value || (sidebarHoverMode && isHovered))\n                                ? 'ml-auto'\n                                : '!hidden',\n                            circle1: 'border-b-default-foreground',\n                            circle2: 'border-b-default-foreground',\n                          }}\n                        />\n                      </>\n                    )}\n                  </NavLink>\n                </NavigationMenuLink>\n              </Tooltip>\n            </NavigationMenuItem>\n            <NavigationMenuItem\n              className={`${navigationItemWidthStyle} text-left transition-[width] duration-200`}\n              value=\"discover\"\n            >\n              <Tooltip\n                content={t('discover')}\n                isDisabled={!sidebarMiniMode.value || (sidebarHoverMode && isHovered)}\n                placement=\"right\"\n                showArrow\n                offset={10}\n              >\n                <NavigationMenuLink asChild>\n                  <NavLink\n                    to=\"/discover\"\n                    className={navigationMenuTriggerStyle({\n                      class: `${navigationItemWidthStyle} h-[56px] justify-start transition-[width] duration-200`,\n                    })}\n                  >\n                    {({ isActive, isPending }) => (\n                      <>\n                        <Discover\n                          className={\n                            !sidebarMiniMode.value || (sidebarHoverMode && isHovered) ? 'mr-4' : ''\n                          }\n                          filled={isActive}\n                        />\n                        {!sidebarMiniMode.value || (sidebarHoverMode && isHovered)\n                          ? t('discover')\n                          : null}\n                        <Spinner\n                          size=\"sm\"\n                          classNames={{\n                            base:\n                              isPending &&\n                              (!sidebarMiniMode.value || (sidebarHoverMode && isHovered))\n                                ? 'ml-auto'\n                                : '!hidden',\n                            circle1: 'border-b-default-foreground',\n                            circle2: 'border-b-default-foreground',\n                          }}\n                        />\n                      </>\n                    )}\n                  </NavLink>\n                </NavigationMenuLink>\n              </Tooltip>\n            </NavigationMenuItem>\n            <NavigationMenuItem\n              className={`${navigationItemWidthStyle} text-left transition-[width] duration-200`}\n              value=\"search\"\n            >\n              <NavigationMenuTrigger\n                className={sidebarActiveStyles({\n                  sidebarMiniMode: sidebarMiniMode.value,\n                  sidebarHoverMode: isHovered,\n                })}\n                showArrow={!sidebarMiniMode.value || (sidebarHoverMode && isHovered)}\n              >\n                <Search\n                  className={\n                    !sidebarMiniMode.value || (sidebarHoverMode && isHovered) ? 'mr-4' : ''\n                  }\n                />\n                {!sidebarMiniMode.value || (sidebarHoverMode && isHovered) ? t('search') : null}\n              </NavigationMenuTrigger>\n              <NavigationMenuContent>\n                <ul className=\"m-0 flex w-fit flex-row gap-x-[6px] p-[6px]\">\n                  <li className=\"m-0 flex flex-col justify-between gap-y-[6px] [&_.active]:bg-content1 [&_.active]:text-content1-foreground\">\n                    <NavigationMenuLink asChild>\n                      <NavLink\n                        to=\"/search/movie\"\n                        className={navigationMenuTriggerStyle({\n                          class:\n                            'flex h-auto w-[215px] flex-row items-center justify-start px-2 hover:bg-background/[0.6] focus:bg-background/[0.6]',\n                        })}\n                      >\n                        {({ isActive, isPending }) => (\n                          <>\n                            <Search className=\"mr-2 h-5 w-5\" filled={isActive} />\n                            {t('search-movies')}\n                            <Spinner\n                              size=\"sm\"\n                              classNames={{\n                                base: isPending ? 'ml-auto' : '!hidden',\n                                circle1: 'border-b-default-foreground',\n                                circle2: 'border-b-default-foreground',\n                              }}\n                            />\n                          </>\n                        )}\n                      </NavLink>\n                    </NavigationMenuLink>\n                    <NavigationMenuLink asChild>\n                      <NavLink\n                        to=\"/search/tv\"\n                        className={navigationMenuTriggerStyle({\n                          class:\n                            'flex h-auto w-[215px] flex-row items-center justify-start px-2 hover:bg-background/[0.6] focus:bg-background/[0.6]',\n                        })}\n                      >\n                        {({ isActive, isPending }) => (\n                          <>\n                            <Search className=\"mr-2 h-5 w-5\" filled={isActive} />\n                            {t('search-tv-shows')}\n                            <Spinner\n                              size=\"sm\"\n                              classNames={{\n                                base: isPending ? 'ml-auto' : '!hidden',\n                                circle1: 'border-b-default-foreground',\n                                circle2: 'border-b-default-foreground',\n                              }}\n                            />\n                          </>\n                        )}\n                      </NavLink>\n                    </NavigationMenuLink>\n                    <NavigationMenuLink asChild>\n                      <NavLink\n                        to=\"/search/anime\"\n                        className={navigationMenuTriggerStyle({\n                          class:\n                            'flex h-auto  w-[215px] flex-row items-center justify-start px-2 hover:bg-background/[0.6] focus:bg-background/[0.6]',\n                        })}\n                      >\n                        {({ isActive, isPending }) => (\n                          <>\n                            <Search className=\"mr-2 h-5 w-5\" filled={isActive} />\n                            {t('search-anime')}\n                            <Spinner\n                              size=\"sm\"\n                              classNames={{\n                                base: isPending ? 'ml-auto' : '!hidden',\n                                circle1: 'border-b-default-foreground',\n                                circle2: 'border-b-default-foreground',\n                              }}\n                            />\n                          </>\n                        )}\n                      </NavLink>\n                    </NavigationMenuLink>\n                    <NavigationMenuLink asChild>\n                      <NavLink\n                        to=\"/search/people\"\n                        className={navigationMenuTriggerStyle({\n                          class:\n                            'flex h-auto w-[215px] flex-row items-center justify-start px-2 hover:bg-background/[0.6] focus:bg-background/[0.6]',\n                        })}\n                      >\n                        {({ isActive, isPending }) => (\n                          <>\n                            <Search className=\"mr-2 h-5 w-5\" filled={isActive} />\n                            {t('search-people')}\n                            <Spinner\n                              size=\"sm\"\n                              classNames={{\n                                base: isPending ? 'ml-auto' : '!hidden',\n                                circle1: 'border-b-default-foreground',\n                                circle2: 'border-b-default-foreground',\n                              }}\n                            />\n                          </>\n                        )}\n                      </NavLink>\n                    </NavigationMenuLink>\n                  </li>\n                </ul>\n              </NavigationMenuContent>\n            </NavigationMenuItem>\n            <NavigationMenuItem\n              className={`${navigationItemWidthStyle} text-left transition-[width] duration-200`}\n              value=\"movies\"\n            >\n              <NavigationMenuTrigger\n                className={sidebarActiveStyles({\n                  sidebarMiniMode: sidebarMiniMode.value,\n                  sidebarHoverMode: isHovered,\n                })}\n                showArrow={!sidebarMiniMode.value || (sidebarHoverMode && isHovered)}\n              >\n                <Movie\n                  className={\n                    !sidebarMiniMode.value || (sidebarHoverMode && isHovered) ? 'mr-4' : ''\n                  }\n                />\n                {!sidebarMiniMode.value || (sidebarHoverMode && isHovered) ? t('movies') : null}\n              </NavigationMenuTrigger>\n              <NavigationMenuContent>\n                <ul className=\"m-0 flex w-fit flex-row gap-x-[6px] p-[6px]\">\n                  <li className=\"m-0 shrink-0 grow-0 basis-[215px]\">\n                    <Link\n                      as={NavLink}\n                      className=\"absolute top-4 z-20 mx-[10px] w-[198px] justify-between text-white after:rounded-small hover:after:bg-white/10 data-[focus-visible=true]:z-20\"\n                      isBlock\n                      showAnchorIcon\n                      color=\"foreground\"\n                      to=\"/discover/movies\"\n                    >\n                      {t('movies-discover')}\n                    </Link>\n                    <Card\n                      radius=\"md\"\n                      isPressable\n                      isFooterBlurred\n                      classNames={{\n                        base: 'w-full border-divider',\n                      }}\n                      role=\"button\"\n                      onPress={() => navigate('/movies')}\n                    >\n                      <CardBody className=\"aspect-[2/3] w-full p-0\">\n                        <Image\n                          radius=\"md\"\n                          width=\"215px\"\n                          height=\"auto\"\n                          style={{\n                            aspectRatio: '2/3',\n                            filter: 'brightness(0.8)',\n                          }}\n                          classNames={{\n                            wrapper: 'min-w-[215px]',\n                          }}\n                          alt=\"Discover movies\"\n                          src=\"https://image.tmdb.org/t/p/w342_filter(duotone,190235,ad47dd)/wNB551TsEb7KFU3an5LwOrgvUpn.jpg\"\n                          loading=\"lazy\"\n                          placeholder=\"empty\"\n                          responsive={[\n                            {\n                              size: {\n                                width: 215,\n                                height: (215 / 2) * 3,\n                              },\n                            },\n                          ]}\n                          dprVariants={[1, 3]}\n                          options={{\n                            contentType: MimeType.WEBP,\n                          }}\n                        />\n                      </CardBody>\n                      <CardFooter className=\"absolute bottom-0 z-[10] justify-start bg-default/60 backdrop-blur-sm\">\n                        <h4 className=\"text-default-foreground\">{t('movies-footer')}</h4>\n                      </CardFooter>\n                    </Card>\n                  </li>\n                  <li className=\"m-0 flex min-w-[215px] flex-col justify-between gap-y-[6px] [&_.active]:bg-content1 [&_.active]:text-content1-foreground\">\n                    <NavigationMenuLink asChild>\n                      <NavLink\n                        to=\"/movies/popular\"\n                        className={navigationMenuTriggerStyle({\n                          class:\n                            'flex h-auto w-[215px] flex-col justify-start px-2 hover:bg-background/[0.6] focus:bg-background/[0.6]',\n                        })}\n                      >\n                        {({ isActive, isPending }) => (\n                          <>\n                            <div className=\"mb-2 flex w-full flex-row items-center justify-start\">\n                              <Discover className=\"mr-2 h-5 w-5\" filled={isActive} />\n                              {t('movies-popular')}\n                              <Spinner\n                                size=\"sm\"\n                                classNames={{\n                                  base: isPending ? 'ml-auto' : '!hidden',\n                                  circle1: 'border-b-default-foreground',\n                                  circle2: 'border-b-default-foreground',\n                                }}\n                              />\n                            </div>\n                            <p className=\"w-full text-xs text-foreground\">\n                              {t('movies-popular-subtitle')}\n                            </p>\n                          </>\n                        )}\n                      </NavLink>\n                    </NavigationMenuLink>\n                    <NavigationMenuLink asChild>\n                      <NavLink\n                        to=\"/movies/now-playing\"\n                        className={navigationMenuTriggerStyle({\n                          class:\n                            'flex h-auto w-[215px] flex-col justify-start px-2 hover:bg-background/[0.6] focus:bg-background/[0.6]',\n                        })}\n                      >\n                        {({ isActive, isPending }) => (\n                          <>\n                            <div className=\"mb-2 flex w-full flex-row items-center justify-start\">\n                              <Discover className=\"mr-2 h-5 w-5\" filled={isActive} />\n                              {t('movies-now-playing')}\n                              <Spinner\n                                size=\"sm\"\n                                classNames={{\n                                  base: isPending ? 'ml-auto' : '!hidden',\n                                  circle1: 'border-b-default-foreground',\n                                  circle2: 'border-b-default-foreground',\n                                }}\n                              />\n                            </div>\n                            <p className=\"w-full text-xs text-foreground\">\n                              {t('movies-now-playing-subtitle')}\n                            </p>\n                          </>\n                        )}\n                      </NavLink>\n                    </NavigationMenuLink>\n                    <NavigationMenuLink asChild>\n                      <NavLink\n                        to=\"/movies/upcoming\"\n                        className={navigationMenuTriggerStyle({\n                          class:\n                            'flex h-auto w-[215px] flex-col justify-start px-2 hover:bg-background/[0.6] focus:bg-background/[0.6]',\n                        })}\n                      >\n                        {({ isActive, isPending }) => (\n                          <>\n                            <div className=\"mb-2 flex w-full flex-row items-center justify-start\">\n                              <Discover className=\"mr-2 h-5 w-5\" filled={isActive} />\n                              {t('movies-upcoming')}\n                              <Spinner\n                                size=\"sm\"\n                                classNames={{\n                                  base: isPending ? 'ml-auto' : '!hidden',\n                                  circle1: 'border-b-default-foreground',\n                                  circle2: 'border-b-default-foreground',\n                                }}\n                              />\n                            </div>\n                            <p className=\"w-full text-xs text-foreground\">\n                              {t('movies-upcoming-subtitle')}\n                            </p>\n                          </>\n                        )}\n                      </NavLink>\n                    </NavigationMenuLink>\n                    <NavigationMenuLink asChild>\n                      <NavLink\n                        to=\"/movies/top-rated\"\n                        className={navigationMenuTriggerStyle({\n                          class:\n                            'flex h-auto w-[215px] flex-col justify-start px-2 hover:bg-background/[0.6] focus:bg-background/[0.6]',\n                        })}\n                      >\n                        {({ isActive, isPending }) => (\n                          <>\n                            <div className=\"mb-2 flex w-full flex-row items-center justify-start\">\n                              <Discover className=\"mr-2 h-5 w-5\" filled={isActive} />\n                              {t('movies-top-rated')}\n                              <Spinner\n                                size=\"sm\"\n                                classNames={{\n                                  base: isPending ? 'ml-auto' : '!hidden',\n                                  circle1: 'border-b-default-foreground',\n                                  circle2: 'border-b-default-foreground',\n                                }}\n                              />\n                            </div>\n                            <p className=\"w-full text-xs text-foreground\">\n                              {t('movies-top-rated-subtitle')}\n                            </p>\n                          </>\n                        )}\n                      </NavLink>\n                    </NavigationMenuLink>\n                  </li>\n                </ul>\n              </NavigationMenuContent>\n            </NavigationMenuItem>\n            <NavigationMenuItem\n              className={`${navigationItemWidthStyle} text-left transition-[width] duration-200`}\n              value=\"tv-shows\"\n            >\n              <NavigationMenuTrigger\n                className={sidebarActiveStyles({\n                  sidebarMiniMode: sidebarMiniMode.value,\n                  sidebarHoverMode: isHovered,\n                })}\n                showArrow={!sidebarMiniMode.value || (sidebarHoverMode && isHovered)}\n              >\n                <Tv\n                  className={\n                    !sidebarMiniMode.value || (sidebarHoverMode && isHovered) ? 'mr-4' : ''\n                  }\n                />\n                {!sidebarMiniMode.value || (sidebarHoverMode && isHovered) ? t('tv-shows') : null}\n              </NavigationMenuTrigger>\n              <NavigationMenuContent>\n                <ul className=\"m-0 flex w-fit flex-row gap-x-[6px] p-[6px]\">\n                  <li className=\"m-0 shrink-0 grow-0 basis-[215px]\">\n                    <Link\n                      as={NavLink}\n                      className=\"absolute top-4 z-20 mx-[10px] w-[198px] justify-between text-white after:rounded-small hover:after:bg-white/10 data-[focus-visible=true]:z-20\"\n                      isBlock\n                      showAnchorIcon\n                      color=\"foreground\"\n                      to=\"/discover/tv-shows\"\n                    >\n                      {t('tv-shows-discover')}\n                    </Link>\n                    <Card\n                      role=\"button\"\n                      radius=\"md\"\n                      isPressable\n                      isFooterBlurred\n                      classNames={{\n                        base: 'w-full border-divider',\n                      }}\n                      onPress={() => navigate('/tv-shows')}\n                    >\n                      <CardBody className=\"aspect-[2/3] w-full p-0\">\n                        <Image\n                          radius=\"md\"\n                          width=\"215px\"\n                          height=\"auto\"\n                          style={{\n                            aspectRatio: '2/3',\n                            filter: 'brightness(0.8)',\n                          }}\n                          classNames={{\n                            wrapper: 'min-w-[215px]',\n                          }}\n                          alt=\"Discover tv shows\"\n                          src=\"https://image.tmdb.org/t/p/w342_filter(duotone,352302,ddd147)/ggFHVNu6YYI5L9pCfOacjizRGt.jpg\"\n                          loading=\"lazy\"\n                          placeholder=\"empty\"\n                          responsive={[\n                            {\n                              size: {\n                                width: 215,\n                                height: (215 / 2) * 3,\n                              },\n                            },\n                          ]}\n                          dprVariants={[1, 3]}\n                          options={{\n                            contentType: MimeType.WEBP,\n                          }}\n                        />\n                      </CardBody>\n                      <CardFooter className=\"absolute bottom-0 z-[10] justify-start bg-default/60 backdrop-blur-sm\">\n                        <h4 className=\"text-default-foreground\">{t('tv-shows-footer')}</h4>\n                      </CardFooter>\n                    </Card>\n                  </li>\n                  <li className=\"m-0 flex min-w-[215px] flex-col justify-between gap-y-[6px] [&_.active]:bg-content1 [&_.active]:text-content1-foreground\">\n                    <NavigationMenuLink asChild>\n                      <NavLink\n                        to=\"/tv-shows/popular\"\n                        className={navigationMenuTriggerStyle({\n                          class:\n                            'flex h-auto w-[225px] flex-col justify-start px-2 hover:bg-background/[0.6] focus:bg-background/[0.6]',\n                        })}\n                      >\n                        {({ isActive, isPending }) => (\n                          <>\n                            <div className=\"mb-2 flex w-full flex-row items-center justify-start\">\n                              <Discover className=\"mr-2 h-5 w-5\" filled={isActive} />\n                              {t('tv-shows-popular')}\n                              <Spinner\n                                size=\"sm\"\n                                classNames={{\n                                  base: isPending ? 'ml-auto' : '!hidden',\n                                  circle1: 'border-b-default-foreground',\n                                  circle2: 'border-b-default-foreground',\n                                }}\n                              />\n                            </div>\n                            <p className=\"w-full text-xs text-foreground\">\n                              {t('tv-shows-popular-subtitle')}\n                            </p>\n                          </>\n                        )}\n                      </NavLink>\n                    </NavigationMenuLink>\n                    <NavigationMenuLink asChild>\n                      <NavLink\n                        to=\"/tv-shows/airing-today\"\n                        className={navigationMenuTriggerStyle({\n                          class:\n                            'flex h-auto w-[225px] flex-col justify-start px-2 hover:bg-background/[0.6] focus:bg-background/[0.6]',\n                        })}\n                      >\n                        {({ isActive, isPending }) => (\n                          <>\n                            <div className=\"mb-2 flex w-full flex-row items-center justify-start\">\n                              <Discover className=\"mr-2 h-5 w-5\" filled={isActive} />\n                              {t('tv-shows-airing-today')}\n                              <Spinner\n                                size=\"sm\"\n                                classNames={{\n                                  base: isPending ? 'ml-auto' : '!hidden',\n                                  circle1: 'border-b-default-foreground',\n                                  circle2: 'border-b-default-foreground',\n                                }}\n                              />\n                            </div>\n                            <p className=\"w-full text-xs text-foreground\">\n                              {t('tv-shows-airing-today-subtitle')}\n                            </p>\n                          </>\n                        )}\n                      </NavLink>\n                    </NavigationMenuLink>\n                    <NavigationMenuLink asChild>\n                      <NavLink\n                        to=\"/tv-shows/on-the-air\"\n                        className={navigationMenuTriggerStyle({\n                          class:\n                            'flex h-auto w-[225px] flex-col justify-start px-2 hover:bg-background/[0.6] focus:bg-background/[0.6]',\n                        })}\n                      >\n                        {({ isActive, isPending }) => (\n                          <>\n                            <div className=\"mb-2 flex w-full flex-row items-center justify-start\">\n                              <Discover className=\"mr-2 h-5 w-5\" filled={isActive} />\n                              {t('tv-shows-on-the-air')}\n                              <Spinner\n                                size=\"sm\"\n                                classNames={{\n                                  base: isPending ? 'ml-auto' : '!hidden',\n                                  circle1: 'border-b-default-foreground',\n                                  circle2: 'border-b-default-foreground',\n                                }}\n                              />\n                            </div>\n                            <p className=\"w-full text-xs text-foreground\">\n                              {t('tv-shows-on-the-air-subtitle')}\n                            </p>\n                          </>\n                        )}\n                      </NavLink>\n                    </NavigationMenuLink>\n                    <NavigationMenuLink asChild>\n                      <NavLink\n                        to=\"/tv-shows/top-rated\"\n                        className={navigationMenuTriggerStyle({\n                          class:\n                            'flex h-auto w-[225px] flex-col justify-start px-2 hover:bg-background/[0.6] focus:bg-background/[0.6]',\n                        })}\n                      >\n                        {({ isActive, isPending }) => (\n                          <>\n                            <div className=\"mb-2 flex w-full flex-row items-center justify-start\">\n                              <Discover className=\"mr-2 h-5 w-5\" filled={isActive} />\n                              {t('tv-shows-top-rated')}\n                              <Spinner\n                                size=\"sm\"\n                                classNames={{\n                                  base: isPending ? 'ml-auto' : '!hidden',\n                                  circle1: 'border-b-default-foreground',\n                                  circle2: 'border-b-default-foreground',\n                                }}\n                              />\n                            </div>\n                            <p className=\"w-full text-xs text-foreground\">\n                              {t('tv-shows-top-rated-subtitle')}\n                            </p>\n                          </>\n                        )}\n                      </NavLink>\n                    </NavigationMenuLink>\n                  </li>\n                </ul>\n              </NavigationMenuContent>\n            </NavigationMenuItem>\n            <NavigationMenuItem\n              className={`${navigationItemWidthStyle} text-left transition-[width] duration-200`}\n              value=\"anime\"\n            >\n              <NavigationMenuTrigger\n                className={sidebarActiveStyles({\n                  sidebarMiniMode: sidebarMiniMode.value,\n                  sidebarHoverMode: isHovered,\n                })}\n                showArrow={!sidebarMiniMode.value || (sidebarHoverMode && isHovered)}\n              >\n                <Anime\n                  className={\n                    !sidebarMiniMode.value || (sidebarHoverMode && isHovered) ? 'mr-4' : ''\n                  }\n                />\n                {!sidebarMiniMode.value || (sidebarHoverMode && isHovered) ? t('anime') : null}\n              </NavigationMenuTrigger>\n              <NavigationMenuContent>\n                <ul className=\"m-0 flex w-fit flex-row gap-x-[6px] p-[6px]\">\n                  <li className=\"m-0 shrink-0 grow-0 basis-[215px]\">\n                    <Link\n                      as={NavLink}\n                      className=\"absolute top-4 z-20 mx-[10px] w-[198px] justify-between text-white after:rounded-small hover:after:bg-white/10 data-[focus-visible=true]:z-20\"\n                      isBlock\n                      showAnchorIcon\n                      color=\"foreground\"\n                      to=\"/discover/anime\"\n                    >\n                      {t('anime-discover')}\n                    </Link>\n                    <Card\n                      role=\"button\"\n                      radius=\"md\"\n                      isPressable\n                      isFooterBlurred\n                      classNames={{\n                        base: 'w-full border-divider',\n                      }}\n                      onPress={() => navigate('/anime')}\n                    >\n                      <CardBody className=\"aspect-[2/3] w-full p-0\">\n                        <Image\n                          radius=\"md\"\n                          width=\"215px\"\n                          height=\"auto\"\n                          style={{\n                            aspectRatio: '2/3',\n                            filter: 'brightness(0.8)',\n                          }}\n                          classNames={{\n                            wrapper: 'min-w-[215px]',\n                          }}\n                          alt=\"Discover anime\"\n                          src=\"https://image.tmdb.org/t/p/w342_filter(duotone,070235,dd4749)/iAld03IP69UEpqQbVWoRBvjqkqX.jpg\"\n                          loading=\"lazy\"\n                          placeholder=\"empty\"\n                          responsive={[\n                            {\n                              size: {\n                                width: 215,\n                                height: (215 / 2) * 3,\n                              },\n                            },\n                          ]}\n                          dprVariants={[1, 3]}\n                          options={{\n                            contentType: MimeType.WEBP,\n                          }}\n                        />\n                      </CardBody>\n                      <CardFooter className=\"absolute bottom-0 z-[10] justify-start bg-default/60 backdrop-blur-sm\">\n                        <h4 className=\"text-default-foreground\">{t('anime-footer')}</h4>\n                      </CardFooter>\n                    </Card>\n                  </li>\n                  <li className=\"m-0 flex min-w-[215px] flex-col justify-between gap-y-[6px] [&_.active]:bg-content1 [&_.active]:text-content1-foreground\">\n                    <NavigationMenuLink asChild>\n                      <NavLink\n                        to=\"/anime/popular\"\n                        className={navigationMenuTriggerStyle({\n                          class:\n                            'flex h-auto w-[225px] flex-col justify-start px-2 hover:bg-background/[0.6] focus:bg-background/[0.6]',\n                        })}\n                      >\n                        {({ isActive, isPending }) => (\n                          <>\n                            <div className=\"mb-2 flex w-full flex-row items-center justify-start\">\n                              <Discover className=\"mr-2 h-5 w-5\" filled={isActive} />\n                              {t('anime-popular')}\n                              <Spinner\n                                size=\"sm\"\n                                classNames={{\n                                  base: isPending ? 'ml-auto' : '!hidden',\n                                  circle1: 'border-b-default-foreground',\n                                  circle2: 'border-b-default-foreground',\n                                }}\n                              />\n                            </div>\n                            <p className=\"w-full text-xs text-foreground\">\n                              {t('anime-popular-subtitle')}\n                            </p>\n                          </>\n                        )}\n                      </NavLink>\n                    </NavigationMenuLink>\n                    <NavigationMenuLink asChild>\n                      <NavLink\n                        to=\"/anime/trending\"\n                        className={navigationMenuTriggerStyle({\n                          class:\n                            'flex h-auto w-[225px] flex-col justify-start px-2 hover:bg-background/[0.6] focus:bg-background/[0.6]',\n                        })}\n                      >\n                        {({ isActive, isPending }) => (\n                          <>\n                            <div className=\"mb-2 flex w-full flex-row items-center justify-start\">\n                              <Discover className=\"mr-2 h-5 w-5\" filled={isActive} />\n                              {t('anime-trending')}\n                              <Spinner\n                                size=\"sm\"\n                                classNames={{\n                                  base: isPending ? 'ml-auto' : '!hidden',\n                                  circle1: 'border-b-default-foreground',\n                                  circle2: 'border-b-default-foreground',\n                                }}\n                              />\n                            </div>\n                            <p className=\"w-full text-xs text-foreground\">\n                              {t('anime-trending-subtitle')}\n                            </p>\n                          </>\n                        )}\n                      </NavLink>\n                    </NavigationMenuLink>\n                    <NavigationMenuLink asChild>\n                      <NavLink\n                        to=\"/anime/recent-episodes\"\n                        className={navigationMenuTriggerStyle({\n                          class:\n                            'flex h-auto w-[225px] flex-col justify-start px-2 hover:bg-background/[0.6] focus:bg-background/[0.6]',\n                        })}\n                      >\n                        {({ isActive, isPending }) => (\n                          <>\n                            <div className=\"mb-2 flex w-full flex-row items-center justify-start\">\n                              <Discover className=\"mr-2 h-5 w-5\" filled={isActive} />\n                              {t('anime-recent-episodes')}\n                              <Spinner\n                                size=\"sm\"\n                                classNames={{\n                                  base: isPending ? 'ml-auto' : '!hidden',\n                                  circle1: 'border-b-default-foreground',\n                                  circle2: 'border-b-default-foreground',\n                                }}\n                              />\n                            </div>\n                            <p className=\"w-full text-xs text-foreground\">\n                              {t('anime-recent-episodes-subtitle')}\n                            </p>\n                          </>\n                        )}\n                      </NavLink>\n                    </NavigationMenuLink>\n                    <NavigationMenuLink asChild>\n                      <NavLink\n                        to=\"/anime/random\"\n                        className={navigationMenuTriggerStyle({\n                          class:\n                            'flex h-auto w-[225px] flex-col justify-start px-2 hover:bg-background/[0.6] focus:bg-background/[0.6]',\n                        })}\n                      >\n                        {({ isPending }) => (\n                          <>\n                            <div className=\"mb-2 flex w-full flex-row items-center justify-start\">\n                              <Discover className=\"mr-2 h-5 w-5\" />\n                              {t('anime-random')}\n                              <Spinner\n                                size=\"sm\"\n                                classNames={{\n                                  base: isPending ? 'ml-auto' : '!hidden',\n                                  circle1: 'border-b-default-foreground',\n                                  circle2: 'border-b-default-foreground',\n                                }}\n                              />\n                            </div>\n                            <p className=\"w-full text-xs text-foreground\">\n                              {t('anime-random-subtitle')}\n                            </p>\n                          </>\n                        )}\n                      </NavLink>\n                    </NavigationMenuLink>\n                  </li>\n                </ul>\n              </NavigationMenuContent>\n            </NavigationMenuItem>\n            <NavigationMenuItem\n              className={`${navigationItemWidthStyle} text-left transition-[width] duration-200`}\n              value=\"people\"\n            >\n              <Tooltip\n                content={t('people')}\n                isDisabled={!sidebarMiniMode.value || (sidebarHoverMode && isHovered)}\n                placement=\"right\"\n                showArrow\n                offset={10}\n              >\n                <NavigationMenuLink asChild>\n                  <NavLink\n                    to=\"/people\"\n                    className={navigationMenuTriggerStyle({\n                      class: `${navigationItemWidthStyle} h-[56px] justify-start transition-[width] duration-200`,\n                    })}\n                  >\n                    {({ isActive, isPending }) => (\n                      <>\n                        <TwoUsers\n                          className={\n                            !sidebarMiniMode.value || (sidebarHoverMode && isHovered) ? 'mr-4' : ''\n                          }\n                          filled={isActive}\n                        />\n                        {!sidebarMiniMode.value || (sidebarHoverMode && isHovered)\n                          ? t('people')\n                          : null}\n                        <Spinner\n                          size=\"sm\"\n                          classNames={{\n                            base:\n                              isPending &&\n                              (!sidebarMiniMode.value || (sidebarHoverMode && isHovered))\n                                ? 'ml-auto'\n                                : '!hidden',\n                            circle1: 'border-b-default-foreground',\n                            circle2: 'border-b-default-foreground',\n                          }}\n                        />\n                      </>\n                    )}\n                  </NavLink>\n                </NavigationMenuLink>\n              </Tooltip>\n            </NavigationMenuItem>\n            <NavigationMenuItem\n              className={`${navigationItemWidthStyle} text-left transition-[width] duration-200`}\n              value=\"featured-lists\"\n            >\n              <Tooltip\n                content={t('featured-lists')}\n                isDisabled={!sidebarMiniMode.value || (sidebarHoverMode && isHovered)}\n                placement=\"right\"\n                showArrow\n                offset={10}\n              >\n                <NavigationMenuLink asChild>\n                  <NavLink\n                    to=\"/lists\"\n                    className={navigationMenuTriggerStyle({\n                      class: `${navigationItemWidthStyle} h-[56px] justify-start transition-[width] duration-200`,\n                    })}\n                  >\n                    {({ isActive, isPending }) => (\n                      <>\n                        <CategoryIcon\n                          className={\n                            !sidebarMiniMode.value || (sidebarHoverMode && isHovered) ? 'mr-4' : ''\n                          }\n                          filled={isActive}\n                        />\n                        {!sidebarMiniMode.value || (sidebarHoverMode && isHovered)\n                          ? t('featured-lists')\n                          : null}\n                        <Spinner\n                          size=\"sm\"\n                          classNames={{\n                            base:\n                              isPending &&\n                              (!sidebarMiniMode.value || (sidebarHoverMode && isHovered))\n                                ? 'ml-auto'\n                                : '!hidden',\n                            circle1: 'border-b-default-foreground',\n                            circle2: 'border-b-default-foreground',\n                          }}\n                        />\n                      </>\n                    )}\n                  </NavLink>\n                </NavigationMenuLink>\n              </Tooltip>\n            </NavigationMenuItem>\n            <NavigationMenuItem\n              className={`${navigationItemWidthStyle} text-left transition-[width] duration-200`}\n              value=\"history\"\n            >\n              <Tooltip\n                content={t('history')}\n                isDisabled={!sidebarMiniMode.value || (sidebarHoverMode && isHovered)}\n                placement=\"right\"\n                showArrow\n                offset={10}\n              >\n                <NavigationMenuLink asChild>\n                  <NavLink\n                    to=\"/watch-history\"\n                    className={navigationMenuTriggerStyle({\n                      class: `${navigationItemWidthStyle} h-[56px] justify-start transition-[width] duration-200`,\n                    })}\n                  >\n                    {({ isActive, isPending }) => (\n                      <>\n                        <History\n                          className={\n                            !sidebarMiniMode.value || (sidebarHoverMode && isHovered) ? 'mr-4' : ''\n                          }\n                          filled={isActive}\n                        />\n                        {!sidebarMiniMode.value || (sidebarHoverMode && isHovered)\n                          ? t('history')\n                          : null}\n                        <Spinner\n                          size=\"sm\"\n                          classNames={{\n                            base:\n                              isPending &&\n                              (!sidebarMiniMode.value || (sidebarHoverMode && isHovered))\n                                ? 'ml-auto'\n                                : '!hidden',\n                            circle1: 'border-b-default-foreground',\n                            circle2: 'border-b-default-foreground',\n                          }}\n                        />\n                      </>\n                    )}\n                  </NavLink>\n                </NavigationMenuLink>\n              </Tooltip>\n            </NavigationMenuItem>\n            <NavigationMenuItem\n              className={`${navigationItemWidthStyle} text-left transition-[width] duration-200`}\n              value=\"settings\"\n            >\n              <Tooltip\n                content={t('settings')}\n                isDisabled={!sidebarMiniMode.value || (sidebarHoverMode && isHovered)}\n                placement=\"right\"\n                showArrow\n                offset={10}\n              >\n                <NavigationMenuLink asChild>\n                  <NavLink\n                    to=\"/settings\"\n                    className={navigationMenuTriggerStyle({\n                      class: `${navigationItemWidthStyle} h-[56px] justify-start transition-[width] duration-200`,\n                    })}\n                  >\n                    {({ isActive, isPending }) => (\n                      <>\n                        <Settings\n                          className={\n                            !sidebarMiniMode.value || (sidebarHoverMode && isHovered) ? 'mr-4' : ''\n                          }\n                          filled={isActive}\n                        />\n                        {!sidebarMiniMode.value || (sidebarHoverMode && isHovered)\n                          ? t('settings')\n                          : null}\n                        <Spinner\n                          size=\"sm\"\n                          classNames={{\n                            base:\n                              isPending &&\n                              (!sidebarMiniMode.value || (sidebarHoverMode && isHovered))\n                                ? 'ml-auto'\n                                : '!hidden',\n                            circle1: 'border-b-default-foreground',\n                            circle2: 'border-b-default-foreground',\n                          }}\n                        />\n                      </>\n                    )}\n                  </NavLink>\n                </NavigationMenuLink>\n              </Tooltip>\n            </NavigationMenuItem>\n          </NavigationMenuList>\n        </NavigationMenu>\n      </div>\n    </aside>\n  );\n};\n\nexport default SideBar;\n"
  },
  {
    "path": "app/components/layouts/TailwindIndicator.tsx",
    "content": "const TailwindIndicator = () => {\n  if (process.env.NODE_ENV === 'production') return null;\n\n  return (\n    <div className=\"fixed bottom-1 left-1 z-[9999] flex h-6 w-6 items-center justify-center rounded-full bg-default p-3 text-xs text-default-foreground\">\n      <div className=\"block sm:hidden\">xs</div>\n      <div className=\"hidden sm:block md:hidden lg:hidden xl:hidden 2xl:hidden\">sm</div>\n      <div className=\"hidden md:block lg:hidden xl:hidden 2xl:hidden\">md</div>\n      <div className=\"hidden lg:block xl:hidden 2xl:hidden\">lg</div>\n      <div className=\"hidden xl:block 2xl:hidden\">xl</div>\n      <div className=\"hidden 2xl:block\">2xl</div>\n    </div>\n  );\n};\n\nexport default TailwindIndicator;\n"
  },
  {
    "path": "app/components/media/MediaDetail.tsx",
    "content": "import { useEffect, useState, type CSSProperties } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { Card, CardBody } from '@nextui-org/card';\nimport { Chip } from '@nextui-org/chip';\nimport { Spacer } from '@nextui-org/spacer';\nimport { Tooltip } from '@nextui-org/tooltip';\nimport { useMeasure, useMediaQuery } from '@react-hookz/web';\nimport { clipboardSupported, copyTextToClipboard, shareData } from '@remix-pwa/client';\nimport { useFetcher, useLocation, useNavigate } from '@remix-run/react';\nimport { shareSupported } from '~/utils';\nimport { motion, useTransform } from 'framer-motion';\nimport { useTranslation } from 'react-i18next';\nimport { MimeType } from 'remix-image';\nimport { toast } from 'sonner';\nimport { tv } from 'tailwind-variants';\nimport tinycolor from 'tinycolor2';\n\nimport type { ColorPalette } from '~/routes/api+/color-palette';\nimport type { IAnimeInfo } from '~/services/consumet/anilist/anilist.types';\nimport type {\n  IMovieDetail,\n  IMovieTranslations,\n  ITvShowDetail,\n  IVideos,\n} from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport useColorDarkenLighten from '~/utils/react/hooks/useColorDarkenLighten';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport { useLayout } from '~/store/layout/useLayout';\nimport { Dialog, DialogContent, DialogTrigger } from '~/components/elements/Dialog';\nimport SelectProvider from '~/components/elements/dialog/SelectProviderDialog';\nimport WatchTrailer, { type Trailer } from '~/components/elements/dialog/WatchTrailerDialog';\nimport Image from '~/components/elements/Image';\nimport Rating from '~/components/elements/shared/Rating';\nimport { backgroundStyles } from '~/components/styles/primitives';\nimport PhotoIcon from '~/assets/icons/PhotoIcon';\nimport ShareIcon from '~/assets/icons/ShareIcon';\n\ninterface IMediaDetail {\n  type: 'movie' | 'tv';\n  item: IMovieDetail | ITvShowDetail | undefined;\n  translations?: IMovieTranslations | undefined;\n  imdbRating: { count: number; star: number } | undefined;\n  color: string | undefined;\n  trailerTime?: number;\n}\n\ninterface IMediaBackground {\n  backdropPath: string | undefined;\n  backgroundColor: string;\n}\n\ninterface IAnimeDetail {\n  item: IAnimeInfo | undefined;\n  trailerTime?: number;\n}\n\nconst backgroundImageStyles = tv({\n  base: 'relative w-full overflow-hidden bg-fixed bg-[left_0px_top_0px] bg-no-repeat',\n  variants: {\n    sidebarMiniMode: {\n      true: 'sm:bg-[left_80px_top_0px]',\n    },\n    sidebarBoxedMode: {\n      true: 'sm:bg-[left_280px_top_0px]',\n    },\n  },\n  compoundVariants: [\n    {\n      sidebarMiniMode: true,\n      sidebarBoxedMode: true,\n      class: 'sm:bg-[left_110px_top_0px]',\n    },\n    {\n      sidebarMiniMode: false,\n      sidebarBoxedMode: false,\n      class: 'sm:bg-[left_250px_top_0px]',\n    },\n  ],\n});\n\nexport const MediaDetail = (props: IMediaDetail) => {\n  const { type, item, imdbRating, color, trailerTime } = props;\n  const [size, ref] = useMeasure<HTMLDivElement>();\n  const [imageSize, imageRef] = useMeasure<HTMLDivElement>();\n  const navigate = useNavigate();\n  const location = useLocation();\n  const isHydrated = useHydrated();\n  const fetcher = useFetcher();\n  const { t } = useTranslation();\n  const { backgroundColor } = useColorDarkenLighten(color);\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const isXl = useMediaQuery('(max-width: 1280px)', { initializeWithValue: false });\n  const [showProvidereDialog, setShowProvidereDialog] = useState(false);\n  const [showTrailerDialog, setShowTrailerDialog] = useState(false);\n  const [trailer, setTrailer] = useState<Trailer>({});\n  const [colorPalette, setColorPalette] = useState<ColorPalette>();\n\n  const { id, tagline, genres, status } = item || {};\n  const title = (item as IMovieDetail)?.title || (item as ITvShowDetail)?.name || '';\n  const titleEng = (item as IMovieDetail)?.titleEng || (item as ITvShowDetail)?.nameEng || '';\n  const orgTitle =\n    (item as IMovieDetail)?.original_title || (item as ITvShowDetail)?.original_name || '';\n  const runtime =\n    // @ts-ignore\n    Number((item as IMovieDetail)?.runtime) ?? Number((item as ITvShowDetail)?.episode_run_time[0]);\n  const posterPath = item?.poster_path\n    ? TMDB?.posterUrl(item?.poster_path || '', 'w342')\n    : undefined;\n  const releaseYear = new Date(\n    (item as IMovieDetail)?.release_date ?? ((item as ITvShowDetail)?.first_air_date || ''),\n  ).getFullYear();\n  const releaseDate = new Date(\n    (item as IMovieDetail)?.release_date ?? ((item as ITvShowDetail)?.first_air_date || ''),\n  ).toLocaleDateString('fr-FR');\n  const description = (item as IMovieDetail)?.overview || (item as ITvShowDetail)?.overview || '';\n\n  useEffect(() => {\n    if (ref.current) {\n      ref.current.scrollIntoView({\n        behavior: 'instant',\n        block: 'center',\n        inline: 'nearest',\n      });\n    }\n  }, [ref, location.pathname]);\n\n  useEffect(() => {\n    if (color?.startsWith('#')) {\n      fetcher.load(`/api/color-palette?color=${color.replace('#', '')}`);\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [color]);\n\n  useEffect(() => {\n    if (fetcher.data && (fetcher.data as { color: ColorPalette }).color) {\n      setColorPalette((fetcher.data as { color: ColorPalette }).color);\n    }\n    if (fetcher.data && (fetcher.data as { videos: IVideos }).videos) {\n      const { results } = (fetcher.data as { videos: IVideos }).videos;\n      const officialTrailer = results.find((result: Trailer) => result.type === 'Trailer');\n      setTrailer(officialTrailer || {});\n    }\n  }, [fetcher.data]);\n\n  const handleShowTrailerDialog = (value: boolean) => {\n    setShowTrailerDialog(value);\n    if (value === true) {\n      fetcher.load(`/${type === 'movie' ? 'movies' : 'tv-shows'}/${id}/videos`);\n    }\n  };\n\n  const handleShare = async () => {\n    const isShareSupported = await shareSupported();\n    if (isShareSupported) {\n      await shareData({\n        title,\n        text: description,\n        url: window.location.href,\n      });\n    } else {\n      const isClipboardSupported = await clipboardSupported();\n      if (isClipboardSupported) {\n        copyTextToClipboard(window.location.href);\n        toast.success('Link copied to clipboard');\n      } else {\n        toast.error('Browser not supported');\n      }\n    }\n  };\n\n  return (\n    <>\n      <Card\n        radius=\"none\"\n        style={\n          {\n            height: `calc(${size?.height}px)`,\n            '--theme-movie-brand': isHydrated ? backgroundColor : 'transparent',\n          } as CSSProperties\n        }\n        classNames={{\n          base: 'flex flex-col w-full !bg-transparent bg-gradient-to-b !from-transparent from-[80px] !to-movie-brand-color border-0 to-[80px] sm:from-[200px] sm:to-[200px] shadow-none',\n        }}\n      >\n        <CardBody\n          // @ts-ignore\n          ref={ref}\n          className=\"z-1 absolute bottom-0 flex grow flex-col items-center justify-center p-0\"\n        >\n          <div className={backgroundStyles({ content: true })} />\n          <div className=\"grid w-full max-w-[1920px] grid-cols-[1fr_2fr] grid-rows-[1fr_auto_auto] items-stretch justify-center gap-x-4 gap-y-6 px-3 pt-5 grid-areas-small sm:grid-rows-[auto_1fr_auto] sm:px-3.5 sm:grid-areas-wide xl:px-4 2xl:px-5\">\n            <div className=\"flex flex-col items-center justify-center grid-in-image\" ref={imageRef}>\n              {posterPath ? (\n                <Image\n                  src={posterPath}\n                  alt={title}\n                  radius=\"lg\"\n                  shadow=\"sm\"\n                  classNames={{\n                    wrapper: 'w-full sm:w-3/4 xl:w-1/2',\n                    img: 'aspect-[2/3] !min-h-[auto] !min-w-[auto]',\n                  }}\n                  disableSkeleton={false}\n                  placeholder=\"empty\"\n                  responsive={[\n                    {\n                      size: {\n                        width: Math.round(\n                          (imageSize?.width || 0) *\n                            (!isXl && !isSm ? 0.5 : isXl && !isSm ? 0.75 : isXl && isSm ? 1 : 1),\n                        ),\n                        height: Math.round(\n                          ((imageSize?.width || 0) *\n                            3 *\n                            (!isXl && !isSm ? 0.5 : isXl && !isSm ? 0.75 : isXl && isSm ? 1 : 1)) /\n                            2,\n                        ),\n                      },\n                    },\n                  ]}\n                  options={{\n                    contentType: MimeType.WEBP,\n                  }}\n                />\n              ) : (\n                <div className=\"z-0 flex aspect-[2/3] h-auto w-full items-center justify-center rounded-large bg-default sm:w-3/4 xl:w-1/2\">\n                  <PhotoIcon width={36} height={36} />\n                </div>\n              )}\n              {isSm ? null : <Spacer y={10} />}\n            </div>\n            <div className=\"flex w-full flex-col items-start justify-start grid-in-title\">\n              <h1 className=\"!text-3xl md:!text-4xl\">\n                {`${title}${isSm ? '' : ` (${releaseYear})`}`}\n              </h1>\n              {tagline ? <p className=\"italic\">{tagline}</p> : null}\n            </div>\n            <div className=\"flex flex-col gap-y-3 grid-in-info sm:gap-y-6\">\n              <div className=\"flex flex-row flex-wrap gap-3\">\n                <Chip\n                  size=\"lg\"\n                  radius=\"full\"\n                  variant=\"flat\"\n                  style={\n                    colorPalette\n                      ? {\n                          backgroundColor: colorPalette[200],\n                          borderColor: colorPalette[400],\n                        }\n                      : { borderColor: '$primaryLightActive' }\n                  }\n                  classNames={{\n                    base: 'duration-200 ease-in-out transition-all',\n                    content: 'flex flex-row items-center gap-x-2',\n                  }}\n                >\n                  <Rating\n                    rating={item?.vote_average?.toFixed(1)}\n                    ratingType=\"movie\"\n                    color={colorPalette ? colorPalette[600] : undefined}\n                  />\n                  {imdbRating ? (\n                    <div className=\"ml-3 flex flex-row items-center gap-x-2\">\n                      <h6 className=\"rounded-large bg-[#ddb600] px-1 text-black\">IMDb</h6>\n                      <h6 style={colorPalette ? { color: colorPalette[600] } : {}}>\n                        {imdbRating?.star}\n                      </h6>\n                    </div>\n                  ) : null}\n                </Chip>\n                <Chip\n                  size=\"lg\"\n                  radius=\"full\"\n                  variant=\"flat\"\n                  className=\"flex flex-row transition-all duration-200 ease-in-out\"\n                  style={\n                    colorPalette\n                      ? {\n                          backgroundColor: colorPalette[200],\n                          borderColor: colorPalette[400],\n                        }\n                      : { borderColor: '$primaryLightActive' }\n                  }\n                >\n                  <h6 style={colorPalette ? { color: colorPalette[600] } : {}}>\n                    {releaseDate}\n                    {runtime ? ` • ${Math.floor(runtime / 60)}h ${runtime % 60}m` : null}\n                  </h6>\n                </Chip>\n              </div>\n              <div className=\"flex w-full flex-row flex-wrap items-center justify-start gap-3\">\n                {genres &&\n                  genres?.map((genre) => (\n                    <Button\n                      type=\"button\"\n                      variant=\"flat\"\n                      key={genre?.id}\n                      size={isSm ? 'sm' : 'md'}\n                      className=\"hover:opacity-80\"\n                      style={{\n                        transition: 'all 0.2s ease-in-out',\n                        ...(colorPalette\n                          ? {\n                              color: colorPalette[600],\n                              backgroundColor: colorPalette[200],\n                            }\n                          : {}),\n                      }}\n                      onPress={() =>\n                        navigate(\n                          `/discover/${type === 'movie' ? 'movies' : 'tv-shows'}?with_genres=${\n                            genre?.id\n                          }`,\n                        )\n                      }\n                    >\n                      {genre?.name}\n                    </Button>\n                  ))}\n              </div>\n            </div>\n            <div className=\"mb-10 flex w-full flex-row flex-wrap items-center justify-between gap-4 grid-in-buttons\">\n              {(status === 'Released' || status === 'Ended' || status === 'Returning Series') && (\n                <Dialog open={showProvidereDialog} onOpenChange={setShowProvidereDialog}>\n                  <DialogTrigger asChild>\n                    <Button\n                      type=\"button\"\n                      // shadow\n                      className=\"w-full bg-gradient-to-br from-secondary to-primary to-50% text-lg font-bold text-primary-foreground sm:w-auto\"\n                      size=\"lg\"\n                    >\n                      {t('watch-now')}\n                    </Button>\n                  </DialogTrigger>\n                  <DialogContent>\n                    <SelectProvider\n                      visible={showProvidereDialog}\n                      closeHandler={() => setShowProvidereDialog(false)}\n                      type={type}\n                      title={titleEng}\n                      origTitle={orgTitle}\n                      year={releaseYear}\n                      id={item?.id}\n                      {...(type === 'tv' && { season: 1, episode: 1, isEnded: status === 'Ended' })}\n                      {...(type === 'movie' && { isEnded: status === 'Released' })}\n                    />\n                  </DialogContent>\n                </Dialog>\n              )}\n              <div className=\"flex flex-row flex-wrap items-center justify-start gap-x-4\">\n                <Dialog\n                  open={showTrailerDialog}\n                  onOpenChange={(value: boolean) => handleShowTrailerDialog(value)}\n                >\n                  <DialogTrigger asChild>\n                    <Button type=\"button\" size={isSm ? 'sm' : 'md'}>\n                      {t('watch-trailer')}\n                    </Button>\n                  </DialogTrigger>\n                  <DialogContent className=\"overflow-hidden !p-0\">\n                    <WatchTrailer trailer={trailer} currentTime={trailerTime} />\n                  </DialogContent>\n                </Dialog>\n                <Tooltip content=\"Share\" placement=\"top\" isDisabled={isSm} showArrow closeDelay={0}>\n                  <Button type=\"button\" size={isSm ? 'sm' : 'md'} onPress={handleShare} isIconOnly>\n                    <ShareIcon />\n                  </Button>\n                </Tooltip>\n              </div>\n            </div>\n          </div>\n        </CardBody>\n      </Card>\n    </>\n  );\n};\n\nexport const AnimeDetail = (props: IAnimeDetail) => {\n  const { t } = useTranslation();\n  const { item, trailerTime } = props;\n  const {\n    id,\n    genres,\n    title,\n    releaseDate,\n    rating,\n    image,\n    type,\n    color,\n    description,\n    status,\n    trailer,\n  } = item || {};\n  const navigate = useNavigate();\n  const location = useLocation();\n  const fetcher = useFetcher();\n  const isHydrated = useHydrated();\n  const [size, ref] = useMeasure<HTMLDivElement>();\n  const [imageSize, imageRef] = useMeasure<HTMLDivElement>();\n  const { backgroundColor } = useColorDarkenLighten(color);\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const isXl = useMediaQuery('(max-width: 1280px)', { initializeWithValue: false });\n  const [showProvidereDialog, setShowProvidereDialog] = useState(false);\n  const [showTrailerDialog, setShowTrailerDialog] = useState(false);\n  const [colorPalette, setColorPalette] = useState<ColorPalette>();\n\n  useEffect(() => {\n    if (ref.current) {\n      ref.current.scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' });\n    }\n  }, [ref, location.pathname]);\n\n  useEffect(() => {\n    if (color?.startsWith('#')) {\n      fetcher.load(`/api/color-palette?color=${color.replace('#', '')}`);\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [color]);\n\n  useEffect(() => {\n    if (fetcher.data && (fetcher.data as { color: ColorPalette }).color) {\n      setColorPalette((fetcher.data as { color: ColorPalette }).color);\n    }\n  }, [fetcher.data]);\n\n  const handleShare = async () => {\n    const isShareSupported = await shareSupported();\n    if (isShareSupported) {\n      await shareData({\n        title: title?.userPreferred,\n        text: description,\n        url: window.location.href,\n      });\n    } else {\n      const isClipboardSupported = await clipboardSupported();\n      if (isClipboardSupported) {\n        copyTextToClipboard(window.location.href);\n        toast.success('Link copied to clipboard');\n      } else {\n        toast.error('Browser not supported');\n      }\n    }\n  };\n\n  return (\n    <>\n      <Card\n        radius=\"none\"\n        style={\n          {\n            height: `calc(${size?.height}px)`,\n            '--theme-movie-brand': isHydrated ? backgroundColor : 'transparent',\n          } as CSSProperties\n        }\n        classNames={{\n          base: 'flex flex-col w-full !bg-transparent bg-gradient-to-b !from-transparent from-[80px] !to-movie-brand-color border-0 to-[80px] sm:from-[200px] sm:to-[200px] shadow-none',\n        }}\n      >\n        <CardBody\n          // @ts-ignore\n          ref={ref}\n          className=\"z-1 absolute bottom-0 flex grow flex-col justify-center p-0\"\n        >\n          <div className={backgroundStyles({ content: true })} />\n          <div className=\"grid w-full max-w-[1920px] grid-cols-[1fr_2fr] grid-rows-[1fr_auto_auto] items-stretch justify-center gap-x-4 gap-y-6 px-3 pt-5 grid-areas-small sm:grid-rows-[auto_1fr_auto] sm:px-3.5 sm:grid-areas-wide xl:px-4 2xl:px-5\">\n            <div className=\"flex flex-col items-center justify-center grid-in-image\" ref={imageRef}>\n              {image ? (\n                <Image\n                  src={image}\n                  title={title?.userPreferred || title?.english || title?.romaji || title?.native}\n                  alt={title?.userPreferred || title?.english || title?.romaji || title?.native}\n                  radius=\"lg\"\n                  shadow=\"sm\"\n                  classNames={{\n                    wrapper: 'w-full sm:w-3/4 xl:w-1/2',\n                    img: 'aspect-[2/3] !min-h-[auto] !min-w-[auto]',\n                  }}\n                  disableSkeleton={false}\n                  placeholder=\"empty\"\n                  responsive={[\n                    {\n                      size: {\n                        width: Math.round(\n                          (imageSize?.width || 0) *\n                            (!isXl && !isSm ? 0.5 : isXl && !isSm ? 0.75 : isXl && isSm ? 1 : 1),\n                        ),\n                        height: Math.round(\n                          ((imageSize?.width || 0) *\n                            3 *\n                            (!isXl && !isSm ? 0.5 : isXl && !isSm ? 0.75 : isXl && isSm ? 1 : 1)) /\n                            2,\n                        ),\n                      },\n                    },\n                  ]}\n                  options={{\n                    contentType: MimeType.WEBP,\n                  }}\n                />\n              ) : (\n                <div className=\"z-0 flex aspect-[2/3] h-auto w-full items-center justify-center rounded-large bg-default sm:w-3/4 xl:w-1/2\">\n                  <PhotoIcon width={36} height={36} />\n                </div>\n              )}\n              {isSm ? null : <Spacer y={10} />}\n            </div>\n            <div className=\"flex w-full flex-col items-start justify-start grid-in-title\">\n              <h1 className=\"!text-3xl md:!text-4xl\">\n                {`${title?.userPreferred || title?.english || title?.romaji || title?.native}`}\n              </h1>\n            </div>\n            <div className=\"flex flex-col gap-y-3 grid-in-info sm:gap-y-6\">\n              <div className=\"flex flex-row flex-wrap gap-3\">\n                <Chip\n                  size=\"lg\"\n                  radius=\"full\"\n                  variant=\"flat\"\n                  className=\"transition-all duration-200 ease-in-out\"\n                  style={\n                    colorPalette\n                      ? {\n                          backgroundColor: colorPalette[200],\n                          borderColor: colorPalette[400],\n                        }\n                      : { borderColor: '$primaryLightActive' }\n                  }\n                >\n                  <Rating\n                    rating={rating}\n                    ratingType=\"anime\"\n                    color={colorPalette ? colorPalette[600] : undefined}\n                  />\n                </Chip>\n                <Chip\n                  size=\"lg\"\n                  radius=\"full\"\n                  variant=\"flat\"\n                  className=\"flex flex-row transition-all duration-200 ease-in-out\"\n                  style={\n                    colorPalette\n                      ? {\n                          backgroundColor: colorPalette[200],\n                          borderColor: colorPalette[400],\n                        }\n                      : { borderColor: '$primaryLightActive' }\n                  }\n                >\n                  <h6 style={colorPalette ? { color: colorPalette[600] } : {}}>\n                    {type}\n                    {releaseDate ? ` • ${releaseDate}` : ''}\n                  </h6>\n                </Chip>\n              </div>\n              <div className=\"flex w-full flex-row flex-wrap items-center justify-start gap-3\">\n                {genres &&\n                  genres?.map((genre) => (\n                    <Button\n                      type=\"button\"\n                      variant=\"flat\"\n                      key={genre}\n                      size={isSm ? 'sm' : 'md'}\n                      className=\"hover:opacity-80\"\n                      style={{\n                        transition: 'all 0.2s ease-in-out',\n                        ...(colorPalette\n                          ? {\n                              color: colorPalette[600],\n                              backgroundColor: colorPalette[200],\n                            }\n                          : {}),\n                      }}\n                      onPress={() => navigate(`/discover/anime?genres=${genre}`)}\n                    >\n                      {genre}\n                    </Button>\n                  ))}\n              </div>\n            </div>\n            <div className=\"mb-10 flex w-full flex-row flex-wrap items-center justify-between gap-4 grid-in-buttons\">\n              <Dialog open={showProvidereDialog} onOpenChange={setShowProvidereDialog}>\n                <DialogTrigger asChild>\n                  <Button\n                    type=\"button\"\n                    size=\"lg\"\n                    className=\"w-full bg-gradient-to-br from-secondary to-primary to-50% text-lg font-bold text-primary-foreground sm:w-auto\"\n                  >\n                    {t('watch-now')}\n                  </Button>\n                </DialogTrigger>\n                <DialogContent>\n                  <SelectProvider\n                    visible={showProvidereDialog}\n                    closeHandler={() => setShowProvidereDialog(false)}\n                    type=\"anime\"\n                    id={id}\n                    title={title?.english || ''}\n                    origTitle={title?.native || ''}\n                    year={Number(releaseDate)}\n                    episode={1}\n                    season={undefined}\n                    animeType={type?.toLowerCase() || 'tv'}\n                    isEnded={status === 'FINISHED'}\n                  />\n                </DialogContent>\n              </Dialog>\n              <div className=\"flex flex-row flex-wrap items-center justify-start gap-x-4\">\n                {trailer ? (\n                  <Dialog open={showTrailerDialog} onOpenChange={setShowTrailerDialog}>\n                    <DialogTrigger asChild>\n                      <Button type=\"button\" size={isSm ? 'sm' : 'md'}>\n                        {t('watch-trailer')}\n                      </Button>\n                    </DialogTrigger>\n                    <DialogContent className=\"overflow-hidden !p-0\">\n                      <WatchTrailer trailer={trailer} currentTime={trailerTime} />\n                    </DialogContent>\n                  </Dialog>\n                ) : null}\n                <Tooltip content=\"Share\" placement=\"top\" isDisabled={isSm}>\n                  <Button type=\"button\" size={isSm ? 'sm' : 'md'} onPress={handleShare} isIconOnly>\n                    <ShareIcon />\n                  </Button>\n                </Tooltip>\n              </div>\n            </div>\n          </div>\n        </CardBody>\n      </Card>\n    </>\n  );\n};\n\nexport const MediaBackgroundImage = (props: IMediaBackground) => {\n  const { backdropPath, backgroundColor } = props;\n  const [size, backgroundRef] = useMeasure<HTMLDivElement>();\n  const isHydrated = useHydrated();\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const { sidebarMiniMode, sidebarBoxedMode } = useSoraSettings();\n  const { scrollY } = useLayout((scrollState) => scrollState);\n  const backgroundImageHeight = isSm ? 100 : 300;\n  const height = useTransform(\n    scrollY,\n    [0, 800 - backgroundImageHeight],\n    [backgroundImageHeight, 800],\n  );\n  return (\n    <div\n      ref={backgroundRef}\n      className={backgroundImageStyles({\n        sidebarMiniMode: sidebarMiniMode.value,\n        sidebarBoxedMode: sidebarBoxedMode.value,\n      })}\n      style={{\n        backgroundImage:\n          size?.width !== undefined\n            ? `url(/api/image?src=${encodeURIComponent(\n                backdropPath ||\n                  'https://raw.githubusercontent.com/Khanhtran47/Sora/master/app/assets/images/background-default.jpg',\n              )}&width=${Math.round(size?.width)}&height=${Math.round(\n                size?.height,\n              )}&fit=cover&position=center&background[]=0&background[]=0&background[]=0&background[]=0&quality=80&compressionLevel=9&loop=0&delay=100&crop=null&contentType=image%2Fwebp)`\n            : 'none',\n        aspectRatio: '2 / 1',\n        visibility: size?.width !== undefined ? 'visible' : 'hidden',\n        backgroundSize: `${size?.width}px auto`,\n      }}\n    >\n      <motion.div\n        style={{\n          position: 'absolute',\n          bottom: 0,\n          left: 0,\n          right: 0,\n          width: '100%',\n          height,\n          backgroundImage: isHydrated\n            ? `linear-gradient(to top, ${backgroundColor}, ${tinycolor(backgroundColor).setAlpha(\n                0,\n              )})`\n            : 'none',\n        }}\n      />\n    </div>\n  );\n};\n"
  },
  {
    "path": "app/components/media/MediaList.tsx",
    "content": "import { useMemo, useState } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { Spacer } from '@nextui-org/spacer';\nimport { Tooltip } from '@nextui-org/tooltip';\nimport { useMediaQuery } from '@react-hookz/web';\nimport { useSearchParams } from '@remix-run/react';\nimport { useTranslation } from 'react-i18next';\nimport { tv } from 'tailwind-variants';\n\nimport type { IMedia } from '~/types/media';\nimport type { ILanguage } from '~/services/tmdb/tmdb.types';\nimport { animeSort, sortMovieTvItems } from '~/constants/filterItems';\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '~/components/elements/Select';\nimport Filter from '~/components/elements/shared/Filter';\nimport ListViewChangeButton from '~/components/elements/shared/ListViewChangeButton';\nimport { Sheet, SheetContent, SheetTitle, SheetTrigger } from '~/components/elements/Sheet';\nimport ChevronLeftIcon from '~/assets/icons/ChevronLeftIcon';\nimport ChevronRightIcon from '~/assets/icons/ChevronRightIcon';\nimport FilterIcon from '~/assets/icons/FilterIcon';\nimport Sort from '~/assets/icons/SortIcon';\n\nimport { MediaListBanner, MediaListCard, MediaListGrid } from './list';\n\ninterface IMediaListProps {\n  /**\n   * Require when cover card is true, value is cover items to show\n   * @type {Array<{ id: number; name: string; backdropPath: string }>}\n   * @memberof IMediaListProps\n   * @example\n   * [\n   *  {\n   *    id: 1,\n   *    name: 'Movie name',\n   *    backdropPath: '/backdrop/path'\n   *  }\n   * ]\n   */\n  coverItem?: { id: number; name: string; backdropPath: string }[];\n  /**\n   * Require when pagination is true, loading type is page, value is current page\n   * @type {number}\n   * @memberof IMediaListProps\n   * @example\n   * 1\n   * 2\n   * 3\n   * ...\n   * 10\n   */\n  currentPage?: number;\n  /**\n   * Pass genres movie object\n   * @type {{ [id: string]: string }}\n   * @memberof IMediaListProps\n   * @example\n   * {\n   *  '1': 'Action',\n   *  '2': 'Adventure',\n   *  '3': 'Animation',\n   *  '4': 'Comedy',\n   * }\n   */\n  genresMovie?: { [id: string]: string };\n  /**\n   * Pass genres tv object\n   * @type {{ [id: string]: string }}\n   * @memberof IMediaListProps\n   * @example\n   * {\n   *  '1': 'Action',\n   *  '2': 'Adventure',\n   *  '3': 'Animation',\n   *  '4': 'Comedy',\n   * }\n   */\n  genresTv?: { [id: string]: string };\n  /**\n   * Require when loading type is scroll, value is true if there is a next page\n   * @type {boolean}\n   * @memberof IMediaListProps\n   * @example\n   * true\n   * false\n   */\n  hasNextPage?: boolean;\n  /**\n   * Value is true if the cover card is active\n   * @type {boolean}\n   * @memberof IMediaListProps\n   * @example\n   * true\n   * false\n   */\n  isCoverCard?: boolean;\n  /**\n   * Value is true if this is the list of people's credits\n   * @type {boolean}\n   * @memberof IMediaListProps\n   * @example\n   * true\n   * false\n   */\n  isCreditsCard?: boolean;\n  /**\n   * Value is items to show\n   * @type {Array<IMedia>}\n   * @memberof IMediaListProps\n   * @see IMedia\n   */\n  items?: IMedia[];\n  /**\n   * Value is type of items to show, help to show the correct url, item type and item title\n   * @type {'movie' | 'tv' | 'anime' | 'people' | 'episode' | 'movie-tv'}\n   * @memberof IMediaListProps\n   * @example\n   * 'movie'\n   * 'tv'\n   * 'anime'\n   * 'people'\n   * 'episode'\n   * 'movie-tv'\n   */\n  itemsType?: 'movie' | 'tv' | 'anime' | 'people' | 'episode' | 'movie-tv';\n  /**\n   * Pass languages object\n   * @type {Array<ILanguage>}\n   * @memberof IMediaListProps\n   * @see ILanguage\n   */\n  languages?: ILanguage[];\n  /**\n   * Value is name of the list\n   * @type {string | (() => never)}\n   * @memberof IMediaListProps\n   * @example\n   * 'Popular Movies'\n   * t('Popular Movies')\n   */\n  listName?: string;\n  /**\n   * Value is type of list to show\n   * @type {'slider-card' | 'slider-banner' | 'grid'}\n   * @memberof IMediaListProps\n   * @example\n   * 'slider-card'\n   * 'slider-banner'\n   * 'grid'\n   */\n  listType?: 'slider-card' | 'slider-banner' | 'grid';\n  /**\n   * Value is true if the navigation buttons are active\n   * @type {boolean}\n   * @memberof IMediaListProps\n   * @example\n   * true\n   * false\n   */\n  navigationButtons?: boolean;\n  /**\n   * Require when view more button is true, value is function to execute when view more button is clicked\n   * @memberof IMediaListProps\n   * @example\n   * () => console.log('View more button is clicked')\n   */\n  onClickViewMore?: () => void;\n  /**\n   * Value is provider name, help to show the correct url for episode itemsType\n   * @type {string}\n   * @memberof IMediaListProps\n   * @example\n   * 'Gogo'\n   * 'Zoro'\n   */\n  provider?: string;\n  /**\n   * Value is route name, help to load the correct route when scrolling in scroll loading type\n   * @type {string}\n   * @memberof IMediaListProps\n   * @example\n   * '/anime/popular'\n   */\n  routeName?: string;\n  /**\n   * Value is true if the filter button is active\n   * @type {boolean}\n   * @memberof IMediaListProps\n   * @example\n   * true\n   * false\n   */\n  showFilterButton?: boolean;\n  /**\n   * Value is true if the list type change button is active\n   * @type {boolean}\n   * @memberof IMediaListProps\n   * @example\n   * true\n   * false\n   */\n  showListTypeChangeButton?: boolean;\n  /**\n   * Value is true if the view more button is active\n   * @type {boolean}\n   * @memberof IMediaListProps\n   * @example\n   * true\n   * false\n   */\n  showMoreList?: boolean;\n  /**\n   * Require when pagination is true, value is total pages\n   * @type {number}\n   * @memberof IMediaListProps\n   * @example\n   * 10\n   * 20\n   * 30\n   * ...\n   */\n  totalPages?: number;\n  /**\n   * Value is true if scroll to top list after changing page\n   * @type {boolean}\n   * @memberof IMediaListProps\n   * @example\n   * true\n   * false\n   */\n  scrollToTopListAfterChangePage?: boolean;\n  /**\n   * Value is true if the sort by select is active\n   * @type {boolean}\n   * @memberof IMediaListProps\n   * @example\n   * true\n   * false\n   */\n  showSortBySelect?: boolean;\n}\n\nconst mediaListStyles = tv({\n  base: 'flex w-full max-w-screen-4xl flex-col justify-center',\n  variants: {\n    gap: {\n      none: 'gap-0',\n      normal: 'gap-2',\n      grid: 'gap-6',\n    },\n    alignItems: {\n      start: 'items-start',\n      center: 'items-center',\n      end: 'items-end',\n    },\n  },\n  defaultVariants: {\n    gap: 'normal',\n    alignItems: 'start',\n  },\n});\n\nconst MediaList = (props: IMediaListProps) => {\n  const {\n    coverItem,\n    currentPage,\n    genresMovie,\n    genresTv,\n    hasNextPage,\n    isCoverCard,\n    isCreditsCard,\n    items,\n    itemsType,\n    languages,\n    listName,\n    listType,\n    navigationButtons,\n    onClickViewMore,\n    provider,\n    scrollToTopListAfterChangePage,\n    showFilterButton,\n    showListTypeChangeButton,\n    showMoreList,\n    showSortBySelect,\n    totalPages,\n  } = props;\n  let list;\n  const { t } = useTranslation();\n  const [prevEl, setPrevEl] = useState<HTMLElement | null>(null);\n  const [nextEl, setNextEl] = useState<HTMLElement | null>(null);\n  const [slideProgress, setSlideProgress] = useState<number>(0);\n  const [showFilter, setShowFilter] = useState<boolean>(false);\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const [searchParams, setSearchParams] = useSearchParams();\n  const currentSearchParams = useMemo<{ [key: string]: string }>(() => {\n    const params: { [key: string]: string } = {};\n    searchParams.forEach((value, key) => {\n      params[key] = value;\n    });\n    return params;\n  }, [searchParams]);\n  const [sortBySelected, setSortBySelected] = useState(() =>\n    itemsType === 'anime'\n      ? currentSearchParams?.sort\n      : itemsType === 'movie' || itemsType === 'tv'\n      ? currentSearchParams?.sort_by\n      : undefined,\n  );\n\n  const sortItems =\n    itemsType === 'movie' || itemsType === 'tv'\n      ? sortMovieTvItems\n      : itemsType === 'anime'\n      ? animeSort\n      : undefined;\n\n  switch (listType) {\n    case 'grid':\n      list = (\n        <MediaListGrid\n          coverItem={coverItem}\n          genresMovie={genresMovie}\n          genresTv={genresTv}\n          hasNextPage={hasNextPage}\n          isCoverCard={isCoverCard}\n          items={items}\n          itemsType={itemsType}\n          provider={provider}\n          totalPages={totalPages}\n          currentPage={currentPage}\n          listType={listType}\n          scrollToTopListAfterChangePage={scrollToTopListAfterChangePage}\n          isCreditsCard={isCreditsCard}\n        />\n      );\n      break;\n    case 'slider-banner':\n      list = <MediaListBanner items={items} genresMovie={genresMovie} genresTv={genresTv} />;\n      break;\n    case 'slider-card':\n      list = (\n        <MediaListCard\n          coverItem={coverItem}\n          genresMovie={genresMovie}\n          genresTv={genresTv}\n          isCoverCard={isCoverCard}\n          items={items}\n          itemsType={itemsType}\n          navigation={{ nextEl, prevEl }}\n          provider={provider}\n          setSlideProgress={setSlideProgress}\n        />\n      );\n      break;\n    default:\n  }\n\n  const handleSelectChange = (value: string) => {\n    setSortBySelected(value);\n    if (itemsType === 'movie' || itemsType === 'tv') {\n      setSearchParams({ ...currentSearchParams, sort_by: value, page: '1' });\n    }\n    if (itemsType === 'anime') {\n      setSearchParams({ ...currentSearchParams, sort: value, page: '1' });\n    }\n  };\n\n  return (\n    <div\n      className={mediaListStyles({\n        gap: listType === 'grid' ? 'grid' : 'normal',\n        alignItems: listType === 'grid' || listType === 'slider-banner' ? 'center' : 'start',\n      })}\n    >\n      {listName || showFilterButton || showListTypeChangeButton ? (\n        <div className=\"mt-5 flex w-full flex-row flex-wrap items-center justify-between gap-3\">\n          {listName ? <h2>{listName}</h2> : null}\n          {showFilterButton || showListTypeChangeButton ? (\n            <div className=\"flex flex-row items-center justify-end gap-3\">\n              {showFilterButton ? (\n                <Sheet open={showFilter} onOpenChange={setShowFilter}>\n                  <Tooltip content={t('show-hide-filter')} showArrow closeDelay={0}>\n                    <SheetTrigger asChild>\n                      <Button\n                        type=\"button\"\n                        size=\"md\"\n                        radius=\"lg\"\n                        isIconOnly\n                        className=\"h-10 min-w-[2.5rem]\"\n                        variant=\"ghost\"\n                      >\n                        <FilterIcon />\n                      </Button>\n                    </SheetTrigger>\n                  </Tooltip>\n                  <SheetContent\n                    swipeDownToClose={isSm}\n                    side={isSm ? 'bottom' : 'right'}\n                    size={isSm ? 'xl' : 'sm'}\n                    open={showFilter}\n                    hideCloseButton\n                    onOpenChange={() => setShowFilter(!showFilter)}\n                    className=\"!px-0 md:!px-0\"\n                  >\n                    <SheetTitle className=\"px-0 md:px-6\">{t('filters')}</SheetTitle>\n                    <Spacer y={2.5} />\n                    <Filter\n                      genres={itemsType === 'movie' ? genresMovie : genresTv}\n                      mediaType={itemsType as 'movie' | 'tv' | 'anime'}\n                      languages={languages}\n                    />\n                  </SheetContent>\n                </Sheet>\n              ) : null}\n              {showSortBySelect && sortItems && sortItems?.length > 0 ? (\n                <Select\n                  value={sortBySelected}\n                  onValueChange={(value: string) => handleSelectChange(value)}\n                >\n                  <SelectTrigger\n                    aria-label=\"Select Sort\"\n                    selectIcon={<Sort className=\"h-4 w-4 opacity-50\" />}\n                  >\n                    <SelectValue placeholder={t('sort-by')} />\n                  </SelectTrigger>\n                  <SelectContent position=\"popper\">\n                    {sortItems.map((sort) => (\n                      <SelectItem key={sort} value={sort}>\n                        {t(sort)}\n                      </SelectItem>\n                    ))}\n                  </SelectContent>\n                </Select>\n              ) : null}\n              {showListTypeChangeButton ? <ListViewChangeButton /> : null}\n            </div>\n          ) : null}\n        </div>\n      ) : null}\n      {showMoreList ? (\n        <div className=\"mb-2 flex w-full flex-row flex-wrap items-center justify-between\">\n          <Button\n            type=\"button\"\n            size={isSm ? 'sm' : 'md'}\n            radius=\"full\"\n            variant=\"solid\"\n            onPress={onClickViewMore}\n          >\n            {t('view-more')}\n          </Button>\n          {navigationButtons ? (\n            <div className=\"flex flex-row gap-x-2\">\n              <Button\n                type=\"button\"\n                radius=\"full\"\n                variant=\"solid\"\n                ref={(node) => setPrevEl(node)}\n                className=\"h-8 p-0 hover:opacity-80 sm:h-10 sm:w-10\"\n                aria-label=\"Previous\"\n                isDisabled={slideProgress === 0}\n                isIconOnly\n              >\n                <ChevronLeftIcon height={isSm ? 18 : 24} width={isSm ? 18 : 24} />\n              </Button>\n              <Button\n                type=\"button\"\n                radius=\"full\"\n                variant=\"solid\"\n                ref={(node) => setNextEl(node)}\n                className=\"h-8 p-0 hover:opacity-80 sm:h-10 sm:w-10\"\n                aria-label=\"Next\"\n                isDisabled={slideProgress === 1}\n                isIconOnly\n              >\n                <ChevronRightIcon height={isSm ? 18 : 24} width={isSm ? 18 : 24} />\n              </Button>\n            </div>\n          ) : null}\n        </div>\n      ) : null}\n      {list}\n    </div>\n  );\n};\n\nexport default MediaList;\n"
  },
  {
    "path": "app/components/media/PeopleDetail.tsx",
    "content": "import { Player } from '@lottiefiles/react-lottie-player';\nimport { Link } from '@nextui-org/link';\nimport { Spacer } from '@nextui-org/spacer';\nimport { useMeasure } from '@react-hookz/web';\nimport { useTranslation } from 'react-i18next';\nimport { MimeType } from 'remix-image';\n\nimport type { IPeopleDetail } from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport useColorDarkenLighten from '~/utils/react/hooks/useColorDarkenLighten';\nimport Image from '~/components/elements/Image';\nimport PhotoIcon from '~/assets/icons/PhotoIcon';\nimport ExternalLinkBlack from '~/assets/lotties/external-link-black.json';\nimport ExternalLinkWhite from '~/assets/lotties/external-link-white.json';\nimport FacebookBlack from '~/assets/lotties/lottieflow-social-networks-15-4-000000-easey.json';\nimport FacebookWhite from '~/assets/lotties/lottieflow-social-networks-15-4-FFFFFF-easey.json';\nimport InstagramBlack from '~/assets/lotties/lottieflow-social-networks-15-5-000000-easey.json';\nimport InstagramWhite from '~/assets/lotties/lottieflow-social-networks-15-5-FFFFFF-easey.json';\nimport TwitterBlack from '~/assets/lotties/lottieflow-social-networks-15-10-000000-easey.json';\nimport TwitterWhite from '~/assets/lotties/lottieflow-social-networks-15-10-FFFFFF-easey.json';\n\ninterface IPeopleDetailProps {\n  detail: IPeopleDetail | undefined;\n  externalIds: {\n    facebookId: null | string;\n    instagramId: string | null;\n    twitterId: null | string;\n  };\n}\n\nconst PeopleDetail = (props: IPeopleDetailProps) => {\n  const { detail, externalIds } = props;\n  const { t } = useTranslation();\n  const { isDark } = useColorDarkenLighten();\n  const [size, imageRef] = useMeasure<HTMLImageElement>();\n  const profilePath = detail?.profile_path\n    ? TMDB?.profileUrl(detail?.profile_path || '', 'h632')\n    : undefined;\n  let gender = '';\n  switch (detail?.gender) {\n    case 0:\n      gender = 'Not specified';\n      break;\n    case 1:\n      gender = 'Female';\n      break;\n    case 2:\n      gender = 'Male';\n      break;\n    case 3:\n      gender = 'Non-Binary';\n      break;\n    default:\n  }\n  return (\n    <>\n      {profilePath ? (\n        <Image\n          ref={imageRef}\n          src={profilePath}\n          width=\"100%\"\n          height=\"auto\"\n          alt={detail?.name}\n          loading=\"lazy\"\n          title={detail?.name}\n          classNames={{\n            wrapper: 'flex justify-center w-1/2 m-auto',\n            img: 'aspect-[2/3] min-h-[auto]',\n          }}\n          placeholder=\"empty\"\n          responsive={[\n            {\n              size: {\n                width: Math.round(size?.width || 0),\n                height: Math.round(size?.height || 0),\n              },\n            },\n          ]}\n          options={{\n            contentType: MimeType.WEBP,\n          }}\n        />\n      ) : (\n        <div className=\"flex w-full items-center justify-center\">\n          <div className=\"z-0 flex aspect-[2/3] h-auto w-1/2 items-center justify-center rounded-large bg-default\">\n            <PhotoIcon width={36} height={36} />\n          </div>\n        </div>\n      )}\n      <Spacer y={5} />\n      <h3 className=\"text-center\">{detail?.name}</h3>\n      <Spacer y={5} />\n      {externalIds &&\n        detail &&\n        (externalIds.facebookId ||\n          externalIds.instagramId ||\n          externalIds.twitterId ||\n          detail.homepage) && (\n          <>\n            <div className=\"flex w-full justify-center gap-4\">\n              {externalIds.facebookId ? (\n                <Link href={`https://facebook.com/${externalIds.facebookId}`} isExternal>\n                  <Player\n                    src={isDark ? FacebookWhite : FacebookBlack}\n                    hover\n                    autoplay={false}\n                    speed={0.75}\n                    className=\"h-7 w-7\"\n                    loop\n                  />\n                </Link>\n              ) : null}\n              {externalIds.instagramId ? (\n                <Link href={`https://instagram.com/${externalIds.instagramId}/`} isExternal>\n                  <Player\n                    src={isDark ? InstagramWhite : InstagramBlack}\n                    hover\n                    autoplay={false}\n                    speed={0.75}\n                    className=\"h-7 w-7\"\n                    loop\n                  />\n                </Link>\n              ) : null}\n              {externalIds.twitterId ? (\n                <Link href={`https://twitter.com/${externalIds.twitterId}`} isExternal>\n                  <Player\n                    src={isDark ? TwitterWhite : TwitterBlack}\n                    hover\n                    autoplay={false}\n                    speed={0.75}\n                    className=\"h-7 w-7\"\n                    loop\n                  />\n                </Link>\n              ) : null}\n              {detail.homepage ? (\n                <Link href={detail?.homepage} isExternal>\n                  <Player\n                    src={isDark ? ExternalLinkWhite : ExternalLinkBlack}\n                    hover\n                    autoplay={false}\n                    speed={0.75}\n                    className=\"h-7 w-7\"\n                    loop\n                  />\n                </Link>\n              ) : null}\n            </div>\n            <Spacer y={5} />\n          </>\n        )}\n      <div className=\"flex w-full justify-start sm:justify-center\">\n        <h4 className=\"w-full sm:w-[70%]\">\n          <strong>{t('personal-info')}</strong>\n        </h4>\n      </div>\n      <Spacer y={5} />\n      <div className=\"flex flex-col flex-wrap items-start justify-start gap-y-4 sm:items-center\">\n        <div className=\"mb-2 flex flex-row items-center justify-start gap-x-6 sm:m-0 sm:w-[70%] sm:flex-col sm:items-start\">\n          <h5>{t('known-for')}</h5>\n          <p>{detail?.known_for_department}</p>\n        </div>\n        <div className=\"mb-2 flex flex-row items-center justify-start gap-x-6 sm:m-0 sm:w-[70%] sm:flex-col sm:items-start\">\n          <h5>{t('gender')}</h5>\n          <p>{gender}</p>\n        </div>\n        <div className=\"mb-2 flex flex-row items-center justify-start gap-x-6 sm:m-0 sm:w-[70%] sm:flex-col sm:items-start\">\n          <h5>{t('birthday')}</h5>\n          <p>{detail?.birthday}</p>\n        </div>\n        <div className=\"mb-2 flex flex-row items-center justify-start gap-x-6 sm:m-0 sm:w-[70%] sm:flex-col sm:items-start\">\n          <h5>{t('place-of-birth')}</h5>\n          <p>{detail?.place_of_birth}</p>\n        </div>\n        <div className=\"mb-2 flex flex-row items-start justify-start gap-x-6 sm:m-0 sm:w-[70%] sm:flex-col\">\n          <h5>{t('also-known-as')}</h5>\n          <p>\n            {detail?.also_known_as?.map((name) => (\n              <>\n                <span key={name}>{name}</span>\n                <br />\n              </>\n            ))}\n          </p>\n        </div>\n      </div>\n    </>\n  );\n};\n\nexport default PeopleDetail;\n"
  },
  {
    "path": "app/components/media/item/BannerItem.tsx",
    "content": "import { Skeleton } from '@nextui-org/skeleton';\nimport { useMediaQuery } from '@react-hookz/web';\n\nimport type { Title } from '~/types/media';\nimport type { ITrailer } from '~/services/consumet/anilist/anilist.types';\n\nimport BannerItemDesktop from './BannerItemDesktop';\nimport BannerItemMobile from './BannerItemMobile';\n\ninterface IBannerItemProps {\n  active?: boolean;\n  backdropPath: string;\n  genreIds: number[];\n  genresAnime: string[];\n  genresMovie?: { [id: string]: string };\n  genresTv?: { [id: string]: string };\n  id: number;\n  mediaType: 'movie' | 'tv' | 'anime' | 'people';\n  overview: string;\n  posterPath: string;\n  title: string | Title;\n  trailer?: ITrailer;\n  voteAverage: number;\n}\n\nconst BannerItem = (props: IBannerItemProps) => {\n  const {\n    active,\n    backdropPath,\n    genreIds,\n    genresMovie,\n    genresTv,\n    genresAnime,\n    id,\n    mediaType,\n    overview,\n    posterPath,\n    title,\n    trailer,\n    voteAverage,\n  } = props;\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  if (isSm === true) {\n    return (\n      <BannerItemMobile\n        active={active}\n        posterPath={posterPath}\n        genreIds={genreIds}\n        genresMovie={genresMovie}\n        genresTv={genresTv}\n        id={id}\n        mediaType={mediaType}\n        title={title}\n        voteAverage={voteAverage}\n        genresAnime={genresAnime}\n      />\n    );\n  }\n  if (isSm === false) {\n    return (\n      <BannerItemDesktop\n        active={active}\n        backdropPath={backdropPath}\n        genreIds={genreIds}\n        genresAnime={genresAnime}\n        genresMovie={genresMovie}\n        genresTv={genresTv}\n        id={id}\n        mediaType={mediaType}\n        overview={overview}\n        posterPath={posterPath}\n        title={title}\n        trailer={trailer}\n        voteAverage={voteAverage}\n      />\n    );\n  }\n  return <Skeleton className=\"aspect-[4/5] w-full sm:aspect-[16/8]\" />;\n};\n\nexport default BannerItem;\n"
  },
  {
    "path": "app/components/media/item/BannerItemCompact.tsx",
    "content": "import { forwardRef } from 'react';\nimport { Card, CardBody, CardFooter } from '@nextui-org/card';\nimport { Skeleton } from '@nextui-org/skeleton';\nimport { useHover } from '@react-aria/interactions';\nimport { AnimatePresence, motion } from 'framer-motion';\nimport { MimeType } from 'remix-image';\nimport { tv } from 'tailwind-variants';\n\nimport type { Title } from '~/types/media';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport AspectRatio from '~/components/elements/AspectRatio';\nimport Image from '~/components/elements/Image';\n\ninterface IBannerItemCompactProps {\n  backdropPath: string;\n  title: string | Title;\n  active?: boolean;\n}\n\nconst bannerCompactStyles = tv({\n  base: [\n    'min-h-[135px] min-w-[240px] origin-[center_right] transform-gpu overflow-hidden border-0 transition-all duration-200 ease-in-out',\n    \"after:absolute after:left-0 after:top-0 after:z-10 after:h-[135px] after:bg-gradient-to-r after:from-default after:to-transparent after:transition-all after:duration-400 after:ease-in-out after:content-['']\",\n    'data-[pressed=true]:scale-105',\n  ],\n  variants: {\n    active: {\n      true: [\n        'translate-x-0 scale-100 after:w-[200px] after:opacity-100',\n        'data-[hover=true]:translate-x-0 data-[hover=true]:scale-100',\n      ],\n      false: [\n        'translate-x-[-10px] scale-x-[1.125] scale-y-[1.03] after:w-[150px] after:opacity-0 hover:after:opacity-100',\n        'data-[hover=true]:translate-x-[-5px] data-[hover=true]:scale-x-[1.075] data-[hover=true]:scale-y-[1.015]',\n      ],\n    },\n  },\n});\n\nconst BannerItemCompact = forwardRef<HTMLDivElement, IBannerItemCompactProps>(\n  (props, forwardedRef) => {\n    const { backdropPath, title, active } = props;\n    const { hoverProps, isHovered } = useHover({});\n    const isHydrated = useHydrated();\n    const { isPlayTrailer } = useSoraSettings();\n    const titleItem =\n      typeof title === 'string'\n        ? title\n        : title?.userPreferred || title?.english || title?.romaji || title?.native;\n    return (\n      <AspectRatio ratio={16 / 9} {...hoverProps}>\n        {isHydrated ? (\n          <Card\n            className={bannerCompactStyles({ active })}\n            role=\"figure\"\n            isPressable\n            isHoverable\n            style={{ position: 'unset' }}\n          >\n            <CardBody className=\"p-0\">\n              <Image\n                src={backdropPath}\n                width=\"100%\"\n                height=\"auto\"\n                alt={titleItem}\n                title={titleItem}\n                className=\"min-h-[135px] min-w-[240px] object-cover\"\n                placeholder=\"empty\"\n                options={{\n                  contentType: MimeType.WEBP,\n                }}\n                responsive={[\n                  {\n                    size: {\n                      width: 240,\n                      height: 135,\n                    },\n                  },\n                ]}\n              />\n            </CardBody>\n            <CardFooter className=\"absolute bottom-0 z-20 h-full w-[160px] items-center justify-start pl-6\">\n              <AnimatePresence>\n                {isHovered || active ? (\n                  <motion.h6\n                    initial={{ opacity: 0, x: -10 }}\n                    animate={{ opacity: 1, x: 0 }}\n                    exit={{ opacity: 0, x: -10 }}\n                    transition={{ duration: 0.2, ease: 'easeOut' }}\n                    className=\"m-0 line-clamp-3 text-left font-bold text-default-foreground\"\n                  >\n                    {titleItem?.length && titleItem.length > 40\n                      ? `${titleItem?.slice(0, 40)}...`\n                      : titleItem}\n                  </motion.h6>\n                ) : null}\n              </AnimatePresence>\n            </CardFooter>\n          </Card>\n        ) : (\n          <Skeleton className=\"h-full w-full\" />\n        )}\n        {active && !isPlayTrailer.value ? (\n          <div className=\"absolute bottom-0 z-20 h-[10px] w-full overflow-hidden\">\n            <div\n              ref={forwardedRef}\n              className=\"float-left h-full animate-progressBarStripes rounded-r-[3px] bg-primary bg-[length:40px_40px]\"\n              style={{\n                width: 0,\n                backgroundImage:\n                  'linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent)',\n              }}\n            />\n          </div>\n        ) : null}\n      </AspectRatio>\n    );\n  },\n);\n\nBannerItemCompact.displayName = 'BannerItemCompact';\n\nexport default BannerItemCompact;\n"
  },
  {
    "path": "app/components/media/item/BannerItemDesktop.tsx",
    "content": "/* eslint-disable react-hooks/exhaustive-deps */\nimport { useEffect, useRef, useState } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { Card, CardBody, CardHeader } from '@nextui-org/card';\nimport { Chip } from '@nextui-org/chip';\nimport { useIntersectionObserver, useMeasure, useMediaQuery } from '@react-hookz/web';\nimport { useFetcher, useNavigate } from '@remix-run/react';\nimport { AnimatePresence, motion, useMotionTemplate, useMotionValue } from 'framer-motion';\nimport { useTranslation } from 'react-i18next';\nimport YouTube from 'react-youtube';\nimport { MimeType, Image as RemixImage } from 'remix-image';\nimport { useSwiper } from 'swiper/react';\n\nimport type { Title } from '~/types/media';\nimport type { ITrailer } from '~/services/consumet/anilist/anilist.types';\nimport type { IDetailImages, IImage, IVideos } from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport useCardHoverStore from '~/store/card/useCardHoverStore';\nimport { useLayout } from '~/store/layout/useLayout';\nimport AspectRatio from '~/components/elements/AspectRatio';\nimport type { Trailer } from '~/components/elements/dialog/WatchTrailerDialog';\nimport Image from '~/components/elements/Image';\nimport Rating from '~/components/elements/shared/Rating';\nimport VolumeOff from '~/assets/icons/VolumeOffIcon';\nimport VolumeUp from '~/assets/icons/VolumeUpIcon';\n\nconst variants = {\n  inView: { opacity: 1, x: 0 },\n  outView: { opacity: 0, x: 40 },\n  showTrailer: { opacity: 1, scale: 0.75 },\n};\n\ninterface IBannerItemDesktopProps {\n  active?: boolean;\n  backdropPath: string;\n  genreIds: number[];\n  genresAnime: string[];\n  genresMovie?: { [id: string]: string };\n  genresTv?: { [id: string]: string };\n  id: number;\n  mediaType: 'movie' | 'tv' | 'anime' | 'people';\n  overview: string;\n  posterPath: string;\n  title: string | Title;\n  trailer?: ITrailer;\n  voteAverage: number;\n}\n\nconst BannerItemDesktop = (props: IBannerItemDesktopProps) => {\n  const {\n    active,\n    backdropPath,\n    genreIds,\n    genresAnime,\n    genresMovie,\n    genresTv,\n    id,\n    mediaType,\n    overview,\n    posterPath,\n    title,\n    trailer,\n    voteAverage,\n  } = props;\n  const { t } = useTranslation();\n  const fetcher = useFetcher();\n  const navigate = useNavigate();\n  const [logo, setLogo] = useState<IImage>();\n  const [player, setPlayer] = useState<ReturnType<YouTube['getInternalPlayer']>>();\n  const [isPlayed, setIsPlayed] = useState<boolean>(false);\n  const [showTrailer, setShowTrailer] = useState<boolean>(false);\n  const [trailerBanner, setTrailerBanner] = useState<Trailer>({});\n  const swiper = useSwiper();\n  const cardRef = useRef<HTMLDivElement>(null);\n  const { viewportRef } = useLayout((state) => state);\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const isMd = useMediaQuery('(max-width: 960px)', { initializeWithValue: false });\n  const isLg = useMediaQuery('(max-width: 1280px)', { initializeWithValue: false });\n  const bannerIntersection = useIntersectionObserver(cardRef, { root: viewportRef });\n  const [size, bannerRef] = useMeasure<HTMLDivElement>();\n  const isCardPlaying = useCardHoverStore((state) => state.isCardPlaying);\n  const { isMutedTrailer, isPlayTrailer, isFetchLogo, isShowSpotlight } = useSoraSettings();\n  const mouseX = useMotionValue(0);\n  const mouseY = useMotionValue(0);\n  const mouseRadius = useMotionValue(0);\n  const titleItem =\n    typeof title === 'string'\n      ? title\n      : title?.userPreferred || title?.english || title?.romaji || title?.native;\n\n  const mute = () => {\n    if (!player) return;\n    player.mute();\n    isMutedTrailer.set(true);\n  };\n\n  const unMute = () => {\n    if (!player) return;\n    player.unMute();\n    isMutedTrailer.set(false);\n  };\n\n  const play = () => {\n    if (!player) return;\n    player.playVideo();\n    setIsPlayed(true);\n  };\n\n  const pause = () => {\n    if (!player) return;\n    player.pauseVideo();\n    setIsPlayed(false);\n  };\n\n  useEffect(() => {\n    if (!isPlayTrailer.value === true) {\n      setShowTrailer(false);\n    }\n    if (isPlayTrailer.value && swiper.autoplay.running) {\n      swiper.autoplay.stop();\n    }\n  }, [isPlayTrailer.value]);\n\n  useEffect(() => {\n    if (!player || !isPlayTrailer.value) return;\n    if (\n      bannerIntersection?.isIntersecting &&\n      active &&\n      !isPlayed &&\n      !isCardPlaying &&\n      !document.hidden\n    ) {\n      play();\n    } else if (!bannerIntersection?.isIntersecting && active && isPlayed) {\n      pause();\n    } else if (active && isCardPlaying && isPlayed) {\n      pause();\n    }\n  }, [\n    bannerIntersection?.isIntersecting,\n    isPlayed,\n    player,\n    active,\n    isCardPlaying,\n    isPlayTrailer.value,\n  ]);\n\n  useEffect(() => {\n    const handleVisibility = () => {\n      if (!document.hidden && bannerIntersection?.isIntersecting && active && !isPlayed) {\n        play();\n      } else if (document.hidden && isPlayed && active) {\n        pause();\n      }\n    };\n    document.addEventListener('visibilitychange', handleVisibility);\n    return () => {\n      document.removeEventListener('visibilitychange', handleVisibility);\n    };\n  }, [active, isPlayed]);\n\n  useEffect(() => {\n    // fetch logo and youtube trailer key from tmdb\n    if (active === true && mediaType !== 'anime') {\n      fetcher.load(\n        `/api/media?id=${id}&type=${mediaType}${isPlayTrailer.value ? '&video=true' : ''}${\n          isFetchLogo.value ? '&image=true' : ''\n        }`,\n      );\n      if (!isPlayTrailer.value) {\n        setTrailerBanner({});\n      }\n    }\n  }, [active, isPlayTrailer.value, isFetchLogo.value]);\n\n  useEffect(() => {\n    if (\n      active === true &&\n      fetcher.data &&\n      (fetcher.data as { videos: IVideos }).videos &&\n      bannerIntersection?.isIntersecting &&\n      mediaType !== 'anime'\n    ) {\n      const { results } = (fetcher.data as { videos: IVideos }).videos;\n      const officialTrailer = results.find((result: Trailer) => result.type === 'Trailer') || {};\n      setTrailerBanner(officialTrailer);\n    }\n    if (\n      active === true &&\n      fetcher.data &&\n      (fetcher.data as { images: IDetailImages }).images &&\n      bannerIntersection?.isIntersecting &&\n      mediaType !== 'anime'\n    ) {\n      const { logos } = (fetcher.data as { images: IDetailImages }).images;\n      if (logos && logos.length > 0) setLogo(logos[0]);\n    }\n  }, [fetcher.data]);\n\n  const handleMouseMove = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {\n    const { currentTarget, clientX, clientY } = e;\n    const { top, left } = currentTarget.getBoundingClientRect();\n    mouseX.set(clientX - left);\n    mouseY.set(clientY - top);\n    mouseRadius.set(size?.width ? (size?.width * 2) / 3 : 1000);\n  };\n\n  return (\n    <AspectRatio ratio={16 / 8} ref={bannerRef}>\n      <Card\n        radius=\"none\"\n        className=\"h-full w-full border-0\"\n        ref={cardRef}\n        role=\"figure\"\n        onMouseMove={isShowSpotlight.value ? handleMouseMove : undefined}\n      >\n        <motion.div\n          className=\"pointer-events-none absolute -inset-px rounded-large transition duration-300 group-hover:opacity-100\"\n          style={{\n            background: useMotionTemplate`\n              radial-gradient(\n                ${mouseRadius}px circle at ${mouseX}px ${mouseY}px,\n                hsl(var(--theme-default-400) / 15),\n                transparent 80%\n              )\n            `,\n          }}\n        />\n        <CardHeader className=\"absolute z-10 flex h-full flex-row items-center justify-start gap-5 md:gap-7 lg:gap-9 xl:justify-center 2xl:h-[calc(100%_-_160px)]\">\n          <div className=\"flex w-5/6 flex-col items-start justify-center gap-4 px-10 md:w-3/4 lg:w-2/3\">\n            <AnimatePresence mode=\"popLayout\">\n              {logo ? (\n                <motion.div\n                  key=\"logo\"\n                  layout\n                  animate={\n                    active && !showTrailer\n                      ? 'inView'\n                      : active && showTrailer\n                      ? 'showTrailer'\n                      : 'outView'\n                  }\n                  transition={\n                    active && !showTrailer\n                      ? { duration: 0.5 }\n                      : active && showTrailer\n                      ? { duration: 0.5, delay: 0.5 }\n                      : { duration: 0.5 }\n                  }\n                  variants={variants}\n                  style={{ originX: 0 }}\n                >\n                  <Image\n                    src={TMDB.logoUrl(logo.file_path, isMd ? 'w185' : 'w300')}\n                    alt={titleItem}\n                    title={titleItem}\n                    radius=\"none\"\n                    classNames={{\n                      img: 'w-logo object-contain nextui-sm:w-logo-sm',\n                    }}\n                    loading=\"eager\"\n                    disableSkeleton={false}\n                    style={{\n                      aspectRatio: logo.aspect_ratio,\n                      mixBlendMode: 'color-burn',\n                      // @ts-ignore\n                      '--movie-logo-width':\n                        logo?.aspect_ratio && 185 / logo.aspect_ratio > 85\n                          ? 85 * Number(logo.aspect_ratio)\n                          : 185,\n                      '--movie-logo-width-sm':\n                        logo?.aspect_ratio && 300 / logo.aspect_ratio > 100\n                          ? 100 * Number(logo.aspect_ratio)\n                          : 300,\n                    }}\n                    placeholder=\"empty\"\n                    options={{ contentType: MimeType.WEBP }}\n                  />\n                </motion.div>\n              ) : (\n                <motion.h1\n                  key=\"title\"\n                  layout\n                  className=\"!line-clamp-2\"\n                  animate={active ? 'inView' : 'outView'}\n                  transition={{ duration: 0.5 }}\n                  variants={variants}\n                >\n                  {titleItem}\n                </motion.h1>\n              )}\n              {!showTrailer ? (\n                <motion.div\n                  key=\"info\"\n                  layout\n                  className=\"flex flex-row items-center gap-x-4\"\n                  initial={{ opacity: 0, x: 40 }}\n                  animate={\n                    active && !showTrailer\n                      ? 'inView'\n                      : active && showTrailer\n                      ? 'outView'\n                      : 'outView'\n                  }\n                  exit={{ opacity: 0, x: 40 }}\n                  transition={\n                    active && !showTrailer\n                      ? { duration: 0.5, delay: 0.15 }\n                      : active && showTrailer\n                      ? { duration: 0.5 }\n                      : { duration: 0.5, delay: 0.15 }\n                  }\n                  variants={variants}\n                >\n                  <Rating\n                    rating={mediaType === 'anime' ? voteAverage : Number(voteAverage.toFixed(1))}\n                    ratingType={mediaType}\n                  />\n                  <div className=\"flex flex-row gap-x-2\">\n                    {mediaType === 'anime'\n                      ? genresAnime?.slice(0, 3).map((genre) => (\n                          <Chip key={genre} variant=\"flat\" color=\"default\" radius=\"full\">\n                            {genre}\n                          </Chip>\n                        ))\n                      : genreIds?.slice(0, 3).map((genreId) => {\n                          if (mediaType === 'movie') {\n                            return (\n                              <Chip\n                                key={genresMovie?.[genreId]}\n                                variant=\"flat\"\n                                color=\"default\"\n                                radius=\"full\"\n                              >\n                                {genresMovie?.[genreId]}\n                              </Chip>\n                            );\n                          }\n                          return (\n                            <Chip\n                              key={genresTv?.[genreId]}\n                              variant=\"flat\"\n                              color=\"default\"\n                              radius=\"full\"\n                            >\n                              {genresTv?.[genreId]}\n                            </Chip>\n                          );\n                        })}\n                  </div>\n                </motion.div>\n              ) : null}\n              {!isMd && !showTrailer ? (\n                <motion.p\n                  key=\"overview\"\n                  layout\n                  className=\"!line-clamp-6 text-justify\"\n                  initial={{ opacity: 0, x: 40 }}\n                  animate={\n                    active && !showTrailer\n                      ? 'inView'\n                      : active && showTrailer\n                      ? 'outView'\n                      : 'outView'\n                  }\n                  exit={{ opacity: 0, x: 40 }}\n                  transition={\n                    active && !showTrailer\n                      ? { duration: 0.5, delay: 0.3 }\n                      : active && showTrailer\n                      ? { duration: 0.5 }\n                      : { duration: 0.5, delay: 0.3 }\n                  }\n                  variants={variants}\n                  dangerouslySetInnerHTML={{ __html: overview || '' }}\n                />\n              ) : null}\n              <motion.div\n                key=\"buttons\"\n                layout\n                animate={active ? 'inView' : 'outView'}\n                transition={{ duration: 0.5, delay: 0.45 }}\n                variants={variants}\n              >\n                <Button\n                  type=\"button\"\n                  color=\"primary\"\n                  className=\"font-bold\"\n                  onPress={() =>\n                    navigate(\n                      `/${\n                        mediaType === 'movie'\n                          ? 'movies/'\n                          : mediaType === 'tv'\n                          ? 'tv-shows/'\n                          : 'anime/'\n                      }${id}/`,\n                      {\n                        state: { currentTime: player ? player.playerInfo.currentTime : 0 },\n                      },\n                    )\n                  }\n                >\n                  {t('moreDetails')}\n                </Button>\n              </motion.div>\n            </AnimatePresence>\n          </div>\n          {!isLg ? (\n            <motion.div\n              animate={\n                active && !showTrailer ? 'inView' : active && showTrailer ? 'outView' : 'outView'\n              }\n              transition={{ duration: 1, ease: 'easeInOut' }}\n              variants={{\n                inView: { opacity: 1, scale: 1, x: 0 },\n                outView: { opacity: 0, scale: 0, x: 0 },\n              }}\n              className=\"flex w-1/3 justify-center\"\n            >\n              <Image\n                src={posterPath || ''}\n                alt={titleItem}\n                title={titleItem}\n                width=\"100%\"\n                disableSkeleton={false}\n                classNames={{\n                  wrapper:\n                    'rounded-large shadow-xl shadow-default aspect-[2/3] w-full h-auto min-h-[auto] min-w-[auto] !max-h-[390px] !max-w-[270px] 2xl:!max-h-[477px] 2xl:!max-w-[318px]',\n                  img: 'h-full object-cover',\n                }}\n                loading=\"eager\"\n                placeholder=\"empty\"\n                responsive={[\n                  {\n                    size: {\n                      width: 270,\n                      height: 390,\n                    },\n                    maxWidth: 1400,\n                  },\n                  {\n                    size: {\n                      width: 318,\n                      height: 477,\n                    },\n                  },\n                ]}\n                options={{\n                  contentType: MimeType.WEBP,\n                }}\n              />\n            </motion.div>\n          ) : null}\n        </CardHeader>\n        <CardBody className=\"m-0 overflow-hidden p-0 after:absolute after:bottom-0 after:left-0 after:h-[100px] after:w-full after:bg-gradient-to-b after:from-transparent after:to-background after:content-[''] after:2xl:h-[250px]\">\n          <AnimatePresence>\n            {!showTrailer && size ? (\n              <motion.div\n                initial={{ opacity: 0, scale: 1.2, y: 40 }}\n                animate={\n                  active ? { opacity: 1, scale: 1, y: 0 } : { opacity: 0.3, scale: 1.2, y: 40 }\n                }\n                exit={{ opacity: 0, scale: 1.2, y: 40 }}\n                transition={{ duration: 1, ease: 'easeIn' }}\n                style={{ overflow: 'hidden' }}\n              >\n                <RemixImage\n                  src={backdropPath || ''}\n                  width=\"100%\"\n                  height=\"auto\"\n                  className=\"left-0 top-0 aspect-[2/1] object-cover opacity-30\"\n                  decoding={active ? 'auto' : 'async'}\n                  loading=\"lazy\"\n                  alt={titleItem}\n                  title={titleItem}\n                  placeholder=\"empty\"\n                  responsive={[\n                    {\n                      size: {\n                        width: size?.width,\n                        height: size?.height,\n                      },\n                    },\n                  ]}\n                  options={{\n                    contentType: MimeType.WEBP,\n                  }}\n                />\n              </motion.div>\n            ) : null}\n          </AnimatePresence>\n          {trailerBanner?.key && !isSm && isPlayTrailer.value && active ? (\n            <YouTube\n              videoId={trailerBanner.key}\n              opts={{\n                height: '100%',\n                width: '100%',\n                playerVars: {\n                  // https://developers.google.com/youtube/player_parameters\n                  autoplay: 0,\n                  modestbranding: 1,\n                  controls: 0,\n                  disablekb: 1,\n                  showinfo: 0,\n                  branding: 0,\n                  rel: 0,\n                  autohide: 0,\n                  iv_load_policy: 3,\n                  cc_load_policy: 0,\n                  playsinline: 1,\n                  mute: 1,\n                  origin: 'https://sorachill.vercel.app',\n                },\n              }}\n              onReady={({ target }) => {\n                if (active && bannerIntersection?.isIntersecting) target.playVideo();\n                setPlayer(target);\n                if (!isMutedTrailer.value) target.unMute();\n              }}\n              onPlay={() => {\n                setIsPlayed(true);\n                if (active && bannerIntersection?.isIntersecting) setShowTrailer(true);\n              }}\n              onPause={() => {\n                setShowTrailer(false);\n              }}\n              onEnd={() => {\n                setShowTrailer(false);\n                swiper.slideNext();\n              }}\n              onError={() => {\n                setShowTrailer(false);\n              }}\n              style={{\n                position: 'absolute',\n                top: 0,\n                left: 0,\n                width: '100%',\n                height: '100%',\n              }}\n              className={\n                showTrailer\n                  ? 'relative !-top-[100%] !h-[300%] !w-full overflow-hidden opacity-80'\n                  : 'hidden'\n              }\n            />\n          ) : null}\n          {trailer?.id && trailer?.site === 'youtube' && isPlayTrailer.value && !isSm && active ? (\n            <YouTube\n              videoId={trailer?.id}\n              opts={{\n                height: '100%',\n                width: '100%',\n                playerVars: {\n                  // https://developers.google.com/youtube/player_parameters\n                  autoplay: 0,\n                  modestbranding: 1,\n                  controls: 0,\n                  disablekb: 1,\n                  showinfo: 0,\n                  branding: 0,\n                  rel: 0,\n                  autohide: 0,\n                  iv_load_policy: 3,\n                  cc_load_policy: 0,\n                  playsinline: 1,\n                  mute: 1,\n                  origin: 'https://sorachill.vercel.app',\n                },\n              }}\n              onReady={({ target }) => {\n                if (active && bannerIntersection?.isIntersecting) target.playVideo();\n                setPlayer(target);\n                if (!isMutedTrailer.value) target.unMute();\n              }}\n              onPlay={() => {\n                setIsPlayed(true);\n                setShowTrailer(true);\n              }}\n              onPause={() => {\n                setShowTrailer(false);\n              }}\n              onEnd={() => {\n                setShowTrailer(false);\n              }}\n              onError={() => {\n                setShowTrailer(false);\n              }}\n              style={{\n                position: 'absolute',\n                top: 0,\n                left: 0,\n                width: '100%',\n                height: '100%',\n              }}\n              className={\n                showTrailer\n                  ? 'relative !-top-[100%] !h-[300%] !w-full overflow-hidden opacity-80'\n                  : 'hidden'\n              }\n            />\n          ) : null}\n        </CardBody>\n        {!isSm && showTrailer && (\n          <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}>\n            <Button\n              type=\"button\"\n              color=\"default\"\n              radius=\"full\"\n              variant=\"ghost\"\n              isIconOnly\n              className=\"absolute bottom-20 right-[85px] z-[90] h-11 w-11 cursor-pointer hover:opacity-80 2xl:bottom-[200px]\"\n              aria-label=\"Toggle Mute\"\n              onPress={isMutedTrailer.value ? unMute : mute}\n            >\n              {isMutedTrailer.value ? (\n                <VolumeOff fill=\"currentColor\" />\n              ) : (\n                <VolumeUp fill=\"currentColor\" />\n              )}\n            </Button>\n          </motion.div>\n        )}\n      </Card>\n    </AspectRatio>\n  );\n};\n\nexport default BannerItemDesktop;\n"
  },
  {
    "path": "app/components/media/item/BannerItemMobile.tsx",
    "content": "import { useRef } from 'react';\nimport { Card, CardBody, CardFooter } from '@nextui-org/card';\nimport { Chip } from '@nextui-org/chip';\nimport { useIntersectionObserver, useMeasure } from '@react-hookz/web';\nimport { Link } from '@remix-run/react';\nimport { AnimatePresence, motion } from 'framer-motion';\nimport Balancer from 'react-wrap-balancer';\nimport Image, { MimeType } from 'remix-image';\n\nimport type { Title } from '~/types/media';\nimport { useLayout } from '~/store/layout/useLayout';\nimport AspectRatio from '~/components/elements/AspectRatio';\nimport Star from '~/assets/icons/StarIcon';\n\ninterface IBannerItemMobileProps {\n  active?: boolean;\n  posterPath: string;\n  genreIds: number[];\n  genresMovie?: { [id: string]: string };\n  genresTv?: { [id: string]: string };\n  id: number | string;\n  mediaType: 'movie' | 'tv' | 'anime' | 'people';\n  title: string | Title;\n  voteAverage: number;\n  genresAnime: string[];\n}\n\nconst BannerItemMobile = (props: IBannerItemMobileProps) => {\n  const {\n    active,\n    posterPath,\n    genreIds,\n    genresMovie,\n    genresTv,\n    id,\n    mediaType,\n    title,\n    voteAverage,\n    genresAnime,\n  } = props;\n  const { viewportRef } = useLayout((state) => state);\n  const cardRef = useRef<HTMLAnchorElement>(null);\n  const cardIntersection = useIntersectionObserver(cardRef, { root: viewportRef });\n  const [size, bannerRef] = useMeasure<HTMLDivElement>();\n  const titleItem =\n    typeof title === 'string'\n      ? title\n      : title?.userPreferred || title?.english || title?.romaji || title?.native;\n\n  return (\n    <AspectRatio ratio={4 / 5} ref={bannerRef} className=\"mt-8\">\n      <Card\n        // @ts-ignore\n        ref={cardRef}\n        shadow=\"none\"\n        isPressable\n        className={`h-full w-full rounded-b-none border-0 !transition-[margin,_transform,_background] !duration-300 !ease-in ${\n          !active ? 'mt-6' : ''\n        }`}\n      >\n        <CardBody\n          as={Link}\n          to={`/${\n            mediaType === 'movie' ? 'movies/' : mediaType === 'tv' ? 'tv-shows/' : 'anime/'\n          }${id}/`}\n          className=\"overflow-hidden p-0 after:absolute after:bottom-0 after:left-0 after:h-[calc(100%/2)] after:w-full after:bg-gradient-to-b after:from-transparent after:to-background after:content-['']\"\n        >\n          <AnimatePresence>\n            {size ? (\n              <motion.div\n                initial={{ opacity: 0, scale: 1.2, y: 40 }}\n                animate={\n                  active ? { opacity: 1, scale: 1, y: 0 } : { opacity: 0.3, scale: 1.2, y: 40 }\n                }\n                exit={{ opacity: 0, scale: 1.2, y: 40 }}\n                transition={{ duration: 0.5 }}\n                style={{ overflow: 'hidden' }}\n              >\n                <Image\n                  src={posterPath || ''}\n                  loading=\"lazy\"\n                  decoding={cardIntersection?.isIntersecting ? 'auto' : 'async'}\n                  width=\"100%\"\n                  height=\"auto\"\n                  className=\"aspect-[4/5] object-cover opacity-80\"\n                  alt={titleItem}\n                  title={titleItem}\n                  placeholder=\"empty\"\n                  responsive={[\n                    {\n                      size: {\n                        width: size?.width,\n                        height: (size?.width || 0) * (5 / 4),\n                      },\n                    },\n                  ]}\n                  options={{\n                    contentType: MimeType.WEBP,\n                  }}\n                />\n              </motion.div>\n            ) : null}\n          </AnimatePresence>\n        </CardBody>\n        <CardFooter\n          as={Link}\n          to={`/${\n            mediaType === 'movie' ? 'movies/' : mediaType === 'tv' ? 'tv-shows/' : 'anime/'\n          }${id}/`}\n          className=\"absolute bottom-6 z-[1]\"\n        >\n          <div className=\"flex w-full flex-col items-center justify-center gap-4 py-3\">\n            <h2 className=\"mb-0 text-center font-semibold\">\n              <Balancer>{titleItem}</Balancer>\n            </h2>\n            <div className=\"m-0 flex w-full flex-row gap-x-2\">\n              <Chip\n                variant=\"flat\"\n                color=\"default\"\n                radius=\"full\"\n                classNames={{\n                  content: 'flex items-center gap-x-1',\n                }}\n              >\n                <Star filled width={16} height={16} />\n                {mediaType === 'anime' ? voteAverage : Number(voteAverage.toFixed(1))}\n              </Chip>\n              {mediaType === 'anime' ? (\n                <Chip variant=\"flat\" color=\"default\" radius=\"full\">\n                  {genresAnime[0]}\n                </Chip>\n              ) : mediaType === 'movie' ? (\n                <Chip variant=\"flat\" color=\"default\" radius=\"full\">\n                  {genresMovie?.[genreIds[0]]}\n                </Chip>\n              ) : (\n                <Chip variant=\"flat\" color=\"default\" radius=\"full\">\n                  {genresTv?.[genreIds[0]]}\n                </Chip>\n              )}\n            </div>\n          </div>\n        </CardFooter>\n      </Card>\n    </AspectRatio>\n  );\n};\n\nexport default BannerItemMobile;\n"
  },
  {
    "path": "app/components/media/item/CardItem.tsx",
    "content": "import { useMemo, useRef, useState } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { Card, CardBody, CardFooter } from '@nextui-org/card';\n// import { Tooltip } from '@nextui-org/react';\nimport { useIntersectionObserver, useMeasure } from '@react-hookz/web';\nimport { Link, useNavigate } from '@remix-run/react';\n// import { motion } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport Balancer from 'react-wrap-balancer';\nimport { MimeType } from 'remix-image';\nimport { tv } from 'tailwind-variants';\n\nimport type { IMedia, Title } from '~/types/media';\nimport type { ITrailer } from '~/services/consumet/anilist/anilist.types';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\n// import useCardHoverStore from '~/store/card/useCardHoverStore';\nimport { useLayout } from '~/store/layout/useLayout';\n// import type { Trailer } from '~/components/elements/dialog/WatchTrailerDialog';\nimport Image from '~/components/elements/Image';\nimport {\n  ScrollArea,\n  ScrollBar,\n  ScrollCorner,\n  ScrollViewport,\n} from '~/components/elements/ScrollArea';\nimport Rating from '~/components/elements/shared/Rating';\nimport PhotoIcon from '~/assets/icons/PhotoIcon';\n\ninterface ICardItemProps {\n  backdropPath: string;\n  character: string;\n  color?: string;\n  episodeNumber?: number;\n  episodeTitle?: string;\n  genreIds: number[];\n  genresAnime: string[];\n  genresMovie?: { [id: string]: string };\n  genresTv?: { [id: string]: string };\n  id: number;\n  isCoverCard?: boolean;\n  isCreditsCard?: boolean;\n  isEpisodeCard?: boolean;\n  isSliderCard?: boolean;\n  job: string;\n  knownFor?: IMedia[];\n  linkTo?: string;\n  mediaType: 'movie' | 'tv' | 'anime' | 'people';\n  overview: string;\n  posterPath: string;\n  releaseDate: string | number;\n  title: string | Title;\n  trailer?: ITrailer;\n  voteAverage: number;\n}\n\nconst cardItemStyles = tv({\n  slots: {\n    base: '!w-[164px] data-[hover=true]:ring-2 data-[hover=true]:ring-focus',\n    body: 'aspect-[2/3]',\n    imageContainer: '',\n    image: 'aspect-[2/3]',\n    content: '',\n    footer: '',\n  },\n  variants: {\n    listViewType: {\n      card: {\n        base: '!w-[164px] sm:!w-[180px] md:!w-[200px] lg:!w-[244px] xl:!w-[264px]',\n        body: 'aspect-[2/3] w-full overflow-hidden p-0',\n        imageContainer: 'h-full w-full focus:outline-none',\n        image: 'z-0 aspect-[2/3] !min-h-[auto] !min-w-[auto] !transition-[transform,_opacity]',\n        footer:\n          'flex min-h-[4.875rem] max-w-[164px] flex-col items-start justify-start focus:outline-none focus:ring-2 focus:ring-inset focus:ring-focus sm:max-w-[210px] md:max-w-[200px] lg:max-w-[244px] xl:max-w-[264px]',\n      },\n      detail: {\n        base: '!w-full sm:!w-[480px]',\n        body: 'flex !h-[174px] w-full !flex-row !overflow-hidden p-0 sm:aspect-[5/3] sm:!h-[auto]',\n        imageContainer: 'w-[116px] focus:outline-none sm:w-2/5',\n        image: 'z-0 !h-[174px] !min-h-[auto] !min-w-[116px] sm:aspect-[2/3] sm:!h-[auto]',\n        content: 'flex grow flex-col gap-y-4 p-3 sm:w-3/5',\n        footer:\n          'absolute bottom-0 flex !w-[116px] justify-center border-t border-divider bg-background/[0.6] backdrop-blur-md focus:outline-none focus:ring-2 focus:ring-inset focus:ring-focus sm:!w-2/5',\n      },\n      table: {\n        base: '!w-full',\n        body: 'flex !h-[174px] w-full !flex-row !overflow-hidden p-0',\n        imageContainer: 'w-[116px] focus:outline-none',\n        image: 'z-0 !h-[174px] !min-h-[auto] !min-w-[116px]',\n        content: 'flex grow flex-col gap-y-4 p-3',\n        footer: '',\n      },\n      coverCard: {\n        base: '!w-[280px] sm:!w-[480px]',\n        body: 'aspect-[16/9] w-full overflow-hidden p-0',\n        image:\n          'z-0 aspect-[16/9] !min-h-[auto] !min-w-[auto] overflow-hidden !transition-[transform,_opacity]',\n        footer:\n          'absolute bottom-0 flex justify-center border-t border-divider bg-background/[0.6] backdrop-blur-md',\n      },\n      people: {\n        base: '!w-[164px]',\n        body: 'aspect-[2/3] w-full overflow-hidden p-0',\n        imageContainer: 'h-full w-full focus:outline-none',\n        image:\n          'z-0 aspect-[2/3] !min-h-[auto] !min-w-[auto] overflow-hidden !transition-[transform,_opacity]',\n        footer:\n          'flex min-h-[5.25rem] max-w-[164px] flex-col items-start justify-start focus:outline-none focus:ring-2 focus:ring-inset focus:ring-focus',\n      },\n    },\n  },\n  compoundVariants: [],\n  compoundSlots: [],\n});\n\nconst CardItem = (props: ICardItemProps) => {\n  const {\n    backdropPath,\n    character,\n    color,\n    episodeNumber,\n    episodeTitle,\n    genreIds,\n    genresAnime,\n    genresMovie,\n    genresTv,\n    // id,\n    isCoverCard,\n    isCreditsCard,\n    isEpisodeCard,\n    isSliderCard,\n    job,\n    knownFor,\n    linkTo,\n    mediaType,\n    overview,\n    posterPath,\n    releaseDate,\n    title,\n    // trailer,\n    voteAverage,\n  } = props;\n  // const fetcher = useFetcher();\n  const navigate = useNavigate();\n  // const [_trailerCard, setTrailerCard] = useState<Trailer>({});\n  const [isTooltipVisible] = useState(false);\n  const { listViewType } = useSoraSettings();\n  const [size, imageRef] = useMeasure<HTMLAnchorElement>();\n  const { viewportRef } = useLayout((scrollState) => scrollState);\n  // useEffect(() => {\n  //   if (fetcher.data && fetcher.data.videos) {\n  //     const { results } = fetcher.data.videos;\n  //     const officialTrailer = results.find((result: Trailer) => result.type === 'Trailer');\n  //     setTrailerCard(officialTrailer);\n  //   }\n  // }, [fetcher.data]);\n  const cardRef = useRef<HTMLDivElement>(null);\n  const cardIntersection = useIntersectionObserver(cardRef, {\n    root: viewportRef,\n    rootMargin: '1500px 650px 1500px 650px',\n  });\n  const inView = useMemo(() => {\n    if (isSliderCard) {\n      return true;\n    }\n    return cardIntersection?.isIntersecting;\n  }, [cardIntersection, isSliderCard]);\n\n  const titleItem =\n    typeof title === 'string'\n      ? title\n      : title?.userPreferred || title?.english || title?.romaji || title?.native;\n  const { base, body, imageContainer, image, content, footer } = cardItemStyles({\n    listViewType: isCoverCard\n      ? 'coverCard'\n      : mediaType === 'people'\n      ? 'people'\n      : isSliderCard || isEpisodeCard\n      ? 'card'\n      : isCreditsCard\n      ? 'table'\n      : listViewType?.value === 'card'\n      ? 'card'\n      : listViewType?.value === 'detail'\n      ? 'detail'\n      : listViewType?.value === 'table'\n      ? 'table'\n      : 'card',\n  });\n\n  if (isCoverCard) {\n    return (\n      <Card\n        isHoverable\n        isPressable\n        className={`${base()} ${isSliderCard ? 'my-4' : ''}`}\n        role=\"button\"\n        ref={cardRef}\n        onPress={(e) => {\n          if (e.pointerType === 'keyboard') {\n            navigate(linkTo || '/');\n          }\n        }}\n      >\n        <CardBody\n          className={body()}\n          // @ts-ignore\n          ref={imageRef}\n          as={Link}\n          to={linkTo || '/'}\n        >\n          {size ? (\n            <Image\n              src={backdropPath}\n              width=\"100%\"\n              alt={titleItem}\n              title={titleItem}\n              classNames={{\n                img: image(),\n              }}\n              placeholder=\"empty\"\n              loading=\"lazy\"\n              disableSkeleton={false}\n              isZoomed={!isSliderCard}\n              decoding={inView ? 'async' : 'auto'}\n              options={{ contentType: MimeType.WEBP }}\n              responsive={[\n                {\n                  size: {\n                    width: Math.round(size?.width),\n                    height: Math.round(size?.height),\n                  },\n                },\n              ]}\n            />\n          ) : null}\n        </CardBody>\n        <CardFooter className={footer()} as={Link} to={linkTo || '/'}>\n          <h5 className=\"text-center font-semibold\">\n            <Balancer>{titleItem}</Balancer>\n          </h5>\n        </CardFooter>\n      </Card>\n    );\n  }\n\n  return (\n    <Card\n      as=\"div\"\n      isHoverable\n      isPressable\n      className={`${base()} ${isSliderCard ? 'my-4' : ''}`}\n      role=\"figure\"\n      style={{ opacity: isTooltipVisible && !isMobile ? 0 : 1 }}\n      ref={cardRef}\n      onPress={(e) => {\n        if (e.pointerType === 'keyboard') {\n          navigate(linkTo || '/');\n        }\n      }}\n    >\n      <CardBody className={body()}>\n        <Link className={imageContainer()} to={linkTo || '/'} ref={imageRef}>\n          {size && !isTooltipVisible && inView ? (\n            posterPath ? (\n              <Image\n                src={posterPath || ''}\n                width=\"100%\"\n                alt={titleItem}\n                title={titleItem}\n                loading=\"lazy\"\n                classNames={{\n                  img: image(),\n                }}\n                decoding={inView ? 'async' : 'auto'}\n                disableSkeleton={false}\n                isZoomed={\n                  (listViewType.value === 'card' || mediaType === 'people') &&\n                  !isSliderCard &&\n                  !isCreditsCard\n                }\n                placeholder=\"empty\"\n                options={{ contentType: MimeType.WEBP }}\n                responsive={[\n                  {\n                    size: {\n                      width: Math.round(size?.width),\n                      height: Math.round(size?.height),\n                    },\n                  },\n                ]}\n              />\n            ) : (\n              <div className=\"z-0 flex aspect-[2/3] h-full w-full items-center justify-center rounded-large bg-default\">\n                <PhotoIcon width={48} height={48} />\n              </div>\n            )\n          ) : null}\n        </Link>\n        {listViewType.value === 'detail' &&\n        !isCreditsCard &&\n        !isSliderCard &&\n        !isEpisodeCard &&\n        mediaType !== 'people' &&\n        inView ? (\n          <div className={content()}>\n            <div className=\"flex h-6 flex-row items-center justify-between\">\n              <h6 className=\"hidden 2xs:block\">\n                {`${mediaType.charAt(0).toUpperCase()}${mediaType.slice(1)} • ${releaseDate}`}\n              </h6>\n              <Rating\n                ratingType={mediaType}\n                rating={mediaType === 'anime' ? voteAverage : voteAverage.toFixed(1)}\n                showStarIcon\n              />\n            </div>\n            <ScrollArea\n              type=\"hover\"\n              scrollHideDelay={400}\n              style={{\n                height: 'calc(100% - 5rem)',\n                width: '100%',\n              }}\n            >\n              <ScrollViewport>\n                <p dangerouslySetInnerHTML={{ __html: overview || '' }} className=\"pr-4 text-sm\" />\n              </ScrollViewport>\n              <ScrollBar />\n              <ScrollCorner />\n            </ScrollArea>\n            <div className=\"flex flex-row items-center justify-start gap-x-3\">\n              {mediaType === 'anime'\n                ? genresAnime?.slice(0, 2).map((genre) => (\n                    <Button\n                      key={genre}\n                      type=\"button\"\n                      color=\"default\"\n                      variant=\"solid\"\n                      size=\"sm\"\n                      onPress={() => navigate(`/discover/anime?genres=${genre}`)}\n                    >\n                      {genre}\n                    </Button>\n                  ))\n                : genreIds?.slice(0, 2).map((genreId) => {\n                    if (mediaType === 'movie') {\n                      return (\n                        <Button\n                          key={genreId}\n                          type=\"button\"\n                          color=\"default\"\n                          variant=\"solid\"\n                          size=\"sm\"\n                          onPress={() =>\n                            navigate(`/discover/movies?with_genres=${genresMovie?.[genreId]}`)\n                          }\n                        >\n                          {genresMovie?.[genreId]}\n                        </Button>\n                      );\n                    }\n                    return (\n                      <Button\n                        key={genreId}\n                        type=\"button\"\n                        color=\"default\"\n                        variant=\"solid\"\n                        size=\"sm\"\n                        onPress={() =>\n                          navigate(`/discover/tv-shows?with_genres=${genresTv?.[genreId]}`)\n                        }\n                      >\n                        {genresTv?.[genreId]}\n                      </Button>\n                    );\n                  })}\n            </div>\n          </div>\n        ) : (listViewType.value === 'table' || isCreditsCard) &&\n          !isSliderCard &&\n          !isEpisodeCard &&\n          mediaType !== 'people' &&\n          inView ? (\n          <div className={content()}>\n            <Link\n              to={linkTo || '/'}\n              className=\"line-clamp-1 text-lg font-bold text-foreground focus:outline-none focus:ring-2 focus:ring-focus\"\n            >\n              {titleItem}\n            </Link>\n            <div className=\"flex flex-row items-center justify-between\">\n              <div className=\"flex flex-row items-center justify-start gap-x-3\">\n                {mediaType === 'anime'\n                  ? genresAnime?.slice(0, 2).map((genre, index) => (\n                      <Button\n                        key={genre}\n                        type=\"button\"\n                        color=\"default\"\n                        variant=\"solid\"\n                        size=\"sm\"\n                        onPress={() => navigate(`/discover/anime?genres=${genre}`)}\n                        className={index === 1 ? '!hidden sm:!flex' : ''}\n                      >\n                        {genre}\n                      </Button>\n                    ))\n                  : genreIds?.slice(0, 2).map((genreId, index) => {\n                      if (mediaType === 'movie') {\n                        return (\n                          <Button\n                            key={genreId}\n                            type=\"button\"\n                            color=\"default\"\n                            variant=\"solid\"\n                            size=\"sm\"\n                            onPress={() =>\n                              navigate(`/discover/movies?with_genres=${genresMovie?.[genreId]}`)\n                            }\n                            className={index === 1 ? '!hidden sm:!flex' : ''}\n                          >\n                            {genresMovie?.[genreId]}\n                          </Button>\n                        );\n                      }\n                      return (\n                        <Button\n                          key={genreId}\n                          type=\"button\"\n                          color=\"default\"\n                          variant=\"solid\"\n                          size=\"sm\"\n                          onPress={() =>\n                            navigate(`/discover/tv-shows?with_genres=${genresTv?.[genreId]}`)\n                          }\n                          className={index === 1 ? '!hidden sm:flex' : ''}\n                        >\n                          {genresTv?.[genreId]}\n                        </Button>\n                      );\n                    })}\n              </div>\n              <div className=\"flex h-6 flex-row items-center justify-between gap-x-3\">\n                <h6 className=\"hidden sm:block\">\n                  {`${mediaType.charAt(0).toUpperCase()}${mediaType.slice(1)} • ${releaseDate}`}\n                </h6>\n                <Rating\n                  ratingType={mediaType}\n                  rating={mediaType === 'anime' ? voteAverage : voteAverage.toFixed(1)}\n                  className=\"hidden 2xs:flex\"\n                />\n              </div>\n            </div>\n            <ScrollArea\n              type=\"hover\"\n              scrollHideDelay={400}\n              style={{\n                height: 'calc(100% - 5rem)',\n                width: '100%',\n                boxShadow: 'none',\n              }}\n            >\n              <ScrollViewport>\n                <p dangerouslySetInnerHTML={{ __html: overview || '' }} className=\"pr-4 text-sm\" />\n              </ScrollViewport>\n              <ScrollBar />\n              <ScrollCorner />\n            </ScrollArea>\n          </div>\n        ) : null}\n      </CardBody>\n      {(listViewType.value === 'card' || mediaType === 'people' || isSliderCard || isEpisodeCard) &&\n      !isCreditsCard &&\n      inView ? (\n        <CardFooter className={footer()} as={Link} to={linkTo || '/'}>\n          <h5\n            className=\"!line-clamp-2 w-full text-left font-semibold\"\n            style={{\n              ...(color ? { color } : null),\n              minWidth: `${mediaType === 'people' ? '100px' : '150px'}`,\n            }}\n          >\n            {titleItem}\n          </h5>\n          {isEpisodeCard ? (\n            <p className=\"line-clamp-2 w-full text-left text-sm text-foreground/60\">\n              EP {episodeNumber} - {episodeTitle}\n            </p>\n          ) : null}\n          {mediaType === 'people' ? (\n            <>\n              {knownFor ? (\n                <p className=\"line-clamp-2 w-full text-left text-sm text-foreground/60\">\n                  {knownFor?.map((movie, index) => (\n                    <>\n                      {movie?.title || movie?.originalTitle || movie?.name || movie?.originalName}\n                      {knownFor?.length && (index < knownFor?.length - 1 ? ', ' : '')}\n                    </>\n                  ))}\n                </p>\n              ) : null}\n              {character ? (\n                <p className=\"line-clamp-2 w-full text-left text-sm text-foreground/60\">\n                  {character}\n                </p>\n              ) : null}\n              {job ? (\n                <p className=\"line-clamp-2 w-full text-left text-sm text-foreground/60\">{job}</p>\n              ) : null}\n            </>\n          ) : null}\n        </CardFooter>\n      ) : listViewType.value === 'detail' &&\n        !isSliderCard &&\n        !isEpisodeCard &&\n        !isCreditsCard &&\n        inView ? (\n        <CardFooter className={footer()} as={Link} to={linkTo || '/'}>\n          <h5 className=\"line-clamp-2 font-semibold\">{titleItem}</h5>\n        </CardFooter>\n      ) : null}\n    </Card>\n  );\n};\n\nexport default CardItem;\n"
  },
  {
    "path": "app/components/media/item/HistoryItem.tsx",
    "content": "import { Card, CardBody } from '@nextui-org/card';\nimport { Progress } from '@nextui-org/progress';\nimport { Link } from '@remix-run/react';\nimport { MimeType } from 'remix-image';\n\nimport type { IHistory } from '~/services/supabase';\nimport Image from '~/components/elements/Image';\nimport notFound from '~/assets/images/404.gif';\n\ninterface IHistoryItem {\n  item: IHistory;\n}\n\nconst HistoryItem = ({ item }: IHistoryItem) => {\n  const watched = Math.round((item?.watched / item?.duration) * 100);\n\n  const url = new URL(`http://abc${item?.route}`);\n  if (item?.watched !== 0) {\n    url.searchParams.set('t', item.watched.toString());\n  }\n\n  return (\n    <Link to={url.pathname + url.search} className=\"w-[304px] sm:w-full\">\n      <Card\n        isPressable\n        isHoverable\n        className=\"data-[hover=true]:ring-2 data-[hover=true]:ring-focus sm:!max-h-[171px] sm:w-full\"\n      >\n        <CardBody className=\"flex flex-col flex-nowrap justify-start overflow-hidden p-0 sm:flex-row\">\n          <Image\n            width=\"304px\"\n            height=\"171px\"\n            src={item?.poster || notFound}\n            alt={item?.title}\n            title={item?.title}\n            classNames={{\n              wrapper: 'z-0 m-0 min-h-[171px] min-w-[304px] overflow-hidden',\n            }}\n            placeholder=\"empty\"\n            loading=\"lazy\"\n            options={{ contentType: MimeType.WEBP }}\n            responsive={[{ size: { width: 304, height: 171 } }]}\n          />\n          <div className=\"flex flex-col justify-start p-3\">\n            <h4 className=\"line-clamp-1\">{item?.title}</h4>\n            {item?.season ? <p>SS {item.season}&ensp;-&ensp;</p> : null}\n            {item?.episode ? <p>EP {item.episode.split('-').at(-1)}</p> : null}\n            <p>{new Date(item?.updated_at.toString()).toLocaleString()}</p>\n          </div>\n          {watched > 5 ? (\n            <Progress\n              size=\"sm\"\n              value={watched}\n              color=\"primary\"\n              className=\"!absolute bottom-0 w-full\"\n            />\n          ) : null}\n        </CardBody>\n      </Card>\n    </Link>\n  );\n};\n\nexport default HistoryItem;\n"
  },
  {
    "path": "app/components/media/item/index.tsx",
    "content": "import type { IMedia, Title } from '~/types/media';\nimport type { ITrailer } from '~/services/consumet/anilist/anilist.types';\n\nimport BannerItem from './BannerItem';\nimport CardItem from './CardItem';\n\ninterface IMediaItem {\n  active?: boolean; // help for detecting active banner, require when type is banner\n  backdropPath?: string; // value is backdrop path of media\n  character?: string; // value is character name, can exist when media type is people\n  color?: string; // value is color of banner image, can exist when media type is anime\n  episodeNumber?: number; // value is episode number, require when type is episode\n  episodeTitle?: string; // value is episode title, require when type is episode\n  genreIds?: number[]; // value is genres of a movie or tv-series, require when type is movie or tv\n  genresAnime?: string[]; // value is genres of an anime, require when type is anime\n  genresMovie?: { [id: string]: string }; // value is all genres of movies, require when type is movie\n  genresTv?: { [id: string]: string }; // value is all genres of tv-series, require when type is tv\n  id?: number | string; // value is id of media\n  isCoverCard?: boolean; // value is true if the cover card is active\n  isCreditsCard?: boolean; // value is true if the card is in people's credits\n  isSliderCard?: boolean; // value is true if the card is in slider\n  job?: string; // value is job of a person, can exist when media type is people\n  knownFor?: IMedia[]; // value is known for of a person, can exist when media type is people\n  linkTo?: string; // value is link to media detail page\n  mediaType?: 'movie' | 'tv' | 'anime' | 'people'; // value is type of media\n  overview?: string; // value is overview of media\n  posterPath?: string; // value is poster path of media\n  releaseDate?: string | number; // value is release date of media\n  title?: string | Title; // value is title of media\n  trailer?: ITrailer; // value is trailer of media\n  type: 'banner' | 'card' | 'episode'; // value is type of media item\n  voteAverage?: number; // value is vote average of media\n}\n\nconst MediaItem = (props: IMediaItem) => {\n  const {\n    active,\n    backdropPath,\n    character,\n    color,\n    episodeNumber,\n    episodeTitle,\n    genreIds,\n    genresAnime,\n    genresMovie,\n    genresTv,\n    id,\n    isCoverCard,\n    isCreditsCard,\n    isSliderCard,\n    job,\n    knownFor,\n    linkTo,\n    mediaType,\n    overview,\n    posterPath,\n    releaseDate,\n    title,\n    trailer,\n    type,\n    voteAverage,\n  } = props;\n\n  if (type === 'banner') {\n    return (\n      <BannerItem\n        active={active}\n        backdropPath={backdropPath || ''}\n        genreIds={genreIds || []}\n        genresAnime={genresAnime || []}\n        genresMovie={genresMovie}\n        genresTv={genresTv}\n        id={Number(id)}\n        mediaType={mediaType || 'movie'}\n        overview={overview || ''}\n        posterPath={posterPath || ''}\n        title={title || ''}\n        trailer={trailer}\n        voteAverage={voteAverage || 0}\n      />\n    );\n  }\n  return (\n    <CardItem\n      backdropPath={backdropPath || ''}\n      character={character || ''}\n      color={color}\n      episodeNumber={episodeNumber}\n      episodeTitle={episodeTitle}\n      genreIds={genreIds || []}\n      genresAnime={genresAnime || []}\n      genresMovie={genresMovie}\n      genresTv={genresTv}\n      id={Number(id)}\n      isCoverCard={isCoverCard}\n      isSliderCard={isSliderCard}\n      isEpisodeCard={type === 'episode'}\n      job={job || ''}\n      knownFor={knownFor}\n      linkTo={linkTo || ''}\n      mediaType={mediaType || 'movie'}\n      overview={overview || ''}\n      posterPath={posterPath || ''}\n      releaseDate={releaseDate || ''}\n      title={title || ''}\n      trailer={trailer}\n      voteAverage={voteAverage || 0}\n      isCreditsCard={isCreditsCard}\n    />\n  );\n};\n\nexport default MediaItem;\nexport { default as BannerItem } from './BannerItem';\nexport { default as CartItem } from './CardItem';\n"
  },
  {
    "path": "app/components/media/list/MediaListBanner.tsx",
    "content": "import { forwardRef, useEffect, useRef, useState } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { useMediaQuery } from '@react-hookz/web';\nimport type Swiper from 'swiper';\nimport { Autoplay, Pagination, Thumbs } from 'swiper/modules';\nimport { Swiper as SwiperReact, SwiperSlide, useSwiper } from 'swiper/react';\n\nimport type { IMedia } from '~/types/media';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport ChevronLeftIcon from '~/assets/icons/ChevronLeftIcon';\nimport ChevronRightIcon from '~/assets/icons/ChevronRightIcon';\nimport PlayIcon from '~/assets/icons/PlayIcon';\nimport StopIcon from '~/assets/icons/StopIcon';\n\nimport MediaItem from '../item';\nimport BannerItemCompact from '../item/BannerItemCompact';\n\nconst CustomNavigation = forwardRef<HTMLDivElement, { slot: 'container-end' }>(\n  (props, forwardedRef) => {\n    const { slot } = props;\n    const swiper = useSwiper();\n    const [slideProgress, setSlideProgress] = useState<number>(0);\n\n    const { isPlayTrailer } = useSoraSettings();\n\n    swiper.on('slideChange', (e) => {\n      setSlideProgress(e.progress);\n    });\n\n    return (\n      <div slot={slot} className=\"hidden sm:block\">\n        <Button\n          type=\"button\"\n          color=\"default\"\n          radius=\"full\"\n          variant=\"ghost\"\n          isIconOnly\n          onPress={() => {\n            isPlayTrailer.set(!isPlayTrailer.value);\n            if (isPlayTrailer.value && !swiper.autoplay.running) {\n              swiper.autoplay.start();\n              swiper.autoplay.resume();\n            } else if (!isPlayTrailer.value && swiper.autoplay.running) {\n              swiper.autoplay.stop();\n            }\n          }}\n          className=\"absolute bottom-20 right-[35px] z-[90] h-11 w-11 cursor-pointer hover:opacity-80 2xl:bottom-[200px]\"\n          aria-label=\"Play Trailer\"\n        >\n          {isPlayTrailer.value ? (\n            <StopIcon fill=\"currentColor\" />\n          ) : (\n            <PlayIcon fill=\"currentColor\" filled />\n          )}\n        </Button>\n        <div className=\"hidden sm:block 2xl:hidden\">\n          <Button\n            type=\"button\"\n            color=\"default\"\n            radius=\"full\"\n            variant=\"ghost\"\n            isIconOnly\n            onPress={() => swiper.slidePrev()}\n            className=\"absolute bottom-[10px] right-[85px] z-[90] h-11 w-11 cursor-pointer hover:opacity-80 2xl:bottom-[200px]\"\n            aria-label=\"Previous\"\n            disabled={slideProgress === 0}\n          >\n            <ChevronLeftIcon fill=\"currentColor\" />\n          </Button>\n          <Button\n            type=\"button\"\n            color=\"default\"\n            radius=\"full\"\n            variant=\"ghost\"\n            isIconOnly\n            onPress={() => swiper.slideNext()}\n            className=\"absolute bottom-[10px] right-[35px] z-[90] h-11 w-11 cursor-pointer hover:opacity-80\"\n            aria-label=\"Next\"\n            disabled={slideProgress === 1}\n          >\n            <ChevronRightIcon fill=\"currentColor\" />\n          </Button>\n          <div\n            className=\"absolute bottom-[150px] right-[35px] z-[90] flex h-12 w-12 items-center justify-center text-default-foreground 2xl:bottom-[270px]\"\n            ref={forwardedRef}\n          >\n            <svg\n              viewBox=\"0 0 48 48\"\n              style={{\n                // @ts-ignore\n                '--progress': 1,\n                strokeDashoffset: 'calc(125.6 * (var(--progress)))',\n                strokeDasharray: '125.6',\n              }}\n              className=\"absolute left-0 top-0 z-10 h-full w-full rotate-90 fill-none stroke-default stroke-2\"\n            >\n              <circle cx=\"24\" cy=\"24\" r=\"20\" />\n            </svg>\n            <span />\n          </div>\n        </div>\n      </div>\n    );\n  },\n);\n\nCustomNavigation.displayName = 'CustomNavigation';\n\nconst CustomNavigationThumbs = ({ slot }: { slot: 'container-end' }) => {\n  const swiper = useSwiper();\n  return (\n    <div slot={slot} className=\"hidden sm:block\">\n      <Button\n        type=\"button\"\n        color=\"default\"\n        variant=\"flat\"\n        className=\"absolute left-[2px] top-[60px] z-[90] m-0 h-11 w-min min-w-0 cursor-pointer rounded-small bg-background/60 p-0 backdrop-blur-md\"\n        isIconOnly\n        onPress={() => swiper.slidePrev()}\n        aria-label=\"Previous\"\n      >\n        <ChevronLeftIcon\n          fill=\"currentColor\"\n          filled\n          className=\"scale-75 opacity-80 transition-all duration-200 ease-linear hover:scale-100 hover:opacity-100\"\n        />\n      </Button>\n      <Button\n        type=\"button\"\n        color=\"default\"\n        variant=\"flat\"\n        className=\"absolute right-[2px] top-[60px] z-[90] m-0 h-11 w-min min-w-0 cursor-pointer rounded-small bg-background/60 p-0 backdrop-blur-md\"\n        isIconOnly\n        onPress={() => swiper.slideNext()}\n        aria-label=\"Next\"\n      >\n        <ChevronRightIcon\n          fill=\"currentColor\"\n          filled\n          className=\"scale-75 opacity-80 transition-all duration-200 ease-linear hover:scale-100 hover:opacity-100\"\n        />\n      </Button>\n    </div>\n  );\n};\n\ninterface IMediaListBannerProps {\n  genresMovie?: { [id: string]: string };\n  genresTv?: { [id: string]: string };\n  items?: IMedia[];\n}\n\nconst MediaListBanner = (props: IMediaListBannerProps) => {\n  const { genresMovie, genresTv, items } = props;\n  const isXl = useMediaQuery('(max-width: 1400px)');\n  const [thumbsSwiper, setThumbsSwiper] = useState<Swiper | null>(null);\n  const [activeIndex, setActiveIndex] = useState(0);\n  const progressRef = useRef<HTMLDivElement>(null);\n  const autoplayProgressRef = useRef<HTMLDivElement>(null);\n  const { isPlayTrailer } = useSoraSettings();\n\n  useEffect(() => {\n    if (\n      autoplayProgressRef.current &&\n      autoplayProgressRef.current.firstChild &&\n      autoplayProgressRef.current.lastChild\n    ) {\n      // @ts-ignore\n      autoplayProgressRef.current.firstChild.style.setProperty('--progress', 1);\n      autoplayProgressRef.current.lastChild.textContent = '';\n    }\n    if (progressRef.current) progressRef.current.style.setProperty('width', '0%');\n  }, [isPlayTrailer.value, isXl]);\n\n  return (\n    <section className=\"relative m-0 box-border flex h-full w-full max-w-screen-4xl flex-wrap items-center justify-center p-0\">\n      {items && items?.length > 0 && (\n        <>\n          <SwiperReact\n            modules={[Thumbs, Pagination, Autoplay]}\n            grabCursor\n            spaceBetween={20}\n            slidesPerView={1.15}\n            centeredSlides\n            thumbs={isXl ? undefined : { swiper: thumbsSwiper, multipleActiveThumbs: false }}\n            loop\n            pagination={{\n              enabled: true,\n              dynamicBullets: true,\n              dynamicMainBullets: 3,\n            }}\n            breakpoints={{\n              650: {\n                spaceBetween: 0,\n                slidesPerView: 1,\n                pagination: {\n                  enabled: true,\n                  dynamicBullets: true,\n                },\n              },\n              1400: {\n                spaceBetween: 0,\n                slidesPerView: 1,\n                pagination: {\n                  enabled: false,\n                  dynamicBullets: true,\n                },\n              },\n            }}\n            autoplay={{\n              delay: 8000,\n              disableOnInteraction: false,\n            }}\n            style={{ width: '100%' }}\n            onAutoplayTimeLeft={(_, timeLeft, percentage) => {\n              if (\n                autoplayProgressRef.current &&\n                autoplayProgressRef.current.firstChild &&\n                autoplayProgressRef.current.lastChild\n              ) {\n                // @ts-ignore\n                autoplayProgressRef.current.firstChild.style.setProperty(\n                  '--progress',\n                  1 - percentage,\n                );\n                autoplayProgressRef.current.lastChild.textContent = `${Math.ceil(timeLeft / 1000)}`;\n              }\n              if (progressRef.current) {\n                progressRef.current.style.setProperty(\n                  'width',\n                  `${(Math.abs(percentage) % 1) * 100}%`,\n                );\n              }\n            }}\n            onActiveIndexChange={(swiper) => {\n              setActiveIndex(swiper.realIndex);\n            }}\n          >\n            {items.map((item, index) => (\n              <SwiperSlide\n                key={`${item.id}-${index}-banner`}\n                virtualIndex={index}\n                style={{ width: '100%' }}\n              >\n                {({ isActive }) => (\n                  <MediaItem\n                    active={isActive}\n                    backdropPath={item?.backdropPath}\n                    genreIds={item?.genreIds}\n                    genresAnime={item?.genresAnime}\n                    genresMovie={genresMovie}\n                    genresTv={genresTv}\n                    id={item?.id}\n                    key={`${item.id}-${index}`}\n                    mediaType={item?.mediaType}\n                    overview={item?.overview}\n                    posterPath={item?.posterPath}\n                    title={item?.title}\n                    trailer={item?.trailer}\n                    type=\"banner\"\n                    voteAverage={item?.voteAverage}\n                  />\n                )}\n              </SwiperSlide>\n            ))}\n            <CustomNavigation slot=\"container-end\" ref={autoplayProgressRef} />\n          </SwiperReact>\n          <SwiperReact\n            grabCursor\n            cssMode\n            spaceBetween={15}\n            slidesPerView=\"auto\"\n            slidesPerGroup={1}\n            slidesPerGroupAuto\n            watchSlidesProgress\n            modules={[Thumbs]}\n            onSwiper={setThumbsSwiper}\n            className=\"!absolute bottom-[15px] left-0 !hidden min-h-[150px] w-full 2xl:!block\"\n          >\n            {items.map((item, index) => (\n              <SwiperSlide\n                key={`${item.id}-${index}-banner-thumb`}\n                className={`mx-1 my-2 !h-[135px] !w-[240px] overflow-hidden rounded-large border-4 transition-opacity duration-300 ease-out ${\n                  isPlayTrailer.value\n                    ? `opacity-20 hover:opacity-70 ${activeIndex === index ? 'opacity-90' : ''}`\n                    : 'opacity-100'\n                } ${\n                  activeIndex === index\n                    ? 'border-primary'\n                    : 'border-transparent hover:border-primary-600'\n                }`}\n              >\n                <BannerItemCompact\n                  ref={progressRef}\n                  backdropPath={item?.backdropPath || ''}\n                  title={item?.title || ''}\n                  active={activeIndex === index}\n                />\n              </SwiperSlide>\n            ))}\n            <CustomNavigationThumbs slot=\"container-end\" />\n          </SwiperReact>\n        </>\n      )}\n    </section>\n  );\n};\n\nexport default MediaListBanner;\n"
  },
  {
    "path": "app/components/media/list/MediaListCard.tsx",
    "content": "import { isMobile } from 'react-device-detect';\nimport { FreeMode, Navigation } from 'swiper/modules';\nimport { Swiper, SwiperSlide } from 'swiper/react';\nimport { tv } from 'tailwind-variants';\n\nimport type { IMedia } from '~/types/media';\n\nimport MediaItem from '../item';\n\ninterface IMediaListCardProps {\n  coverItem?: { id: number; name: string; backdropPath: string }[];\n  genresMovie?: { [id: string]: string };\n  genresTv?: { [id: string]: string };\n  isCoverCard?: boolean;\n  items?: IMedia[];\n  itemsType?: 'movie' | 'tv' | 'anime' | 'people' | 'episode' | 'movie-tv';\n  navigation?: { nextEl?: string | HTMLElement | null; prevEl?: string | HTMLElement | null };\n  provider?: string;\n  setSlideProgress?: React.Dispatch<React.SetStateAction<number>>;\n}\n\nconst swiperSlideStyles = tv({\n  variants: {\n    cardType: {\n      coverCard: '!w-[290px] sm:!w-[490px]',\n      card: '!w-[168px] sm:!w-[190px] md:!w-[210px] lg:!w-[250px] xl:!w-[270px]',\n      peopleCard: '!w-[168px]',\n    },\n  },\n});\n\nconst swiperStyles = tv({\n  base: 'w-full [&_.swiper-wrapper]:m-[0_0_1.5rem_1px]',\n});\n\nconst MediaListCard = (props: IMediaListCardProps) => {\n  const {\n    coverItem,\n    genresMovie,\n    genresTv,\n    isCoverCard,\n    items,\n    itemsType,\n    navigation,\n    provider,\n    setSlideProgress,\n  } = props;\n\n  if (isCoverCard) {\n    return (\n      <div className=\"flex w-full items-center justify-start\">\n        {coverItem && coverItem?.length > 0 && (\n          <Swiper\n            className={swiperStyles()}\n            modules={[Navigation, FreeMode]}\n            grabCursor\n            freeMode={{\n              enabled: isMobile,\n              sticky: true,\n              minimumVelocity: 0.1,\n              momentum: true,\n              momentumVelocityRatio: 1,\n              momentumRatio: 1,\n              momentumBounce: true,\n              momentumBounceRatio: 1,\n            }}\n            spaceBetween={10}\n            slidesPerView=\"auto\"\n            slidesPerGroup={1}\n            slidesPerGroupAuto\n            navigation={navigation}\n            onSlideChange={(swiper) => {\n              if (setSlideProgress) {\n                setSlideProgress(swiper.progress);\n              }\n            }}\n          >\n            {coverItem &&\n              coverItem.map((item, index) => {\n                const href = `/lists/${item.id}`;\n                return (\n                  <SwiperSlide\n                    className={swiperSlideStyles({\n                      cardType: 'coverCard',\n                    })}\n                    key={`${item.id}-${index}-cover-card`}\n                  >\n                    <MediaItem\n                      backdropPath={item?.backdropPath}\n                      isCoverCard={isCoverCard}\n                      isSliderCard\n                      linkTo={href}\n                      title={item?.name}\n                      type=\"card\"\n                    />\n                  </SwiperSlide>\n                );\n              })}\n          </Swiper>\n        )}\n      </div>\n    );\n  }\n\n  return (\n    <div className=\"flex w-full items-center justify-start\">\n      {items && items?.length > 0 ? (\n        <Swiper\n          className={swiperStyles()}\n          modules={[Navigation, FreeMode]}\n          grabCursor\n          freeMode={{\n            enabled: isMobile,\n            sticky: true,\n            minimumVelocity: 0.1,\n            momentum: true,\n            momentumVelocityRatio: 1,\n            momentumRatio: 1,\n            momentumBounce: true,\n            momentumBounceRatio: 1,\n          }}\n          spaceBetween={10}\n          slidesPerView=\"auto\"\n          slidesPerGroup={1}\n          slidesPerGroupAuto\n          navigation={navigation}\n          onSlideChange={(swiper) => {\n            if (setSlideProgress) {\n              setSlideProgress(swiper.progress);\n            }\n          }}\n        >\n          {items &&\n            items.map((item, index) => {\n              const href =\n                itemsType && itemsType === 'episode'\n                  ? `/anime/${item.id}/episode/${item.episodeNumber}/watch?provider=${provider}`\n                  : itemsType === 'anime'\n                  ? `/anime/${item.id}/`\n                  : itemsType === 'people'\n                  ? `/people/${item.id}/`\n                  : itemsType === 'movie'\n                  ? `/movies/${item.id}/`\n                  : itemsType === 'tv'\n                  ? `/tv-shows/${item.id}/`\n                  : itemsType === 'movie-tv' && item?.mediaType === 'movie'\n                  ? `/movies/${item.id}/`\n                  : itemsType === 'movie-tv' && item?.mediaType === 'tv'\n                  ? `/tv-shows/${item.id}/`\n                  : '/';\n\n              return (\n                <SwiperSlide\n                  key={`${item.id}-${index}-card-${itemsType}`}\n                  className={swiperSlideStyles({\n                    cardType: item?.mediaType === 'people' ? 'peopleCard' : 'card',\n                  })}\n                >\n                  <MediaItem\n                    backdropPath={item?.backdropPath}\n                    character={item?.character}\n                    color={item?.color}\n                    episodeNumber={item?.episodeNumber}\n                    episodeTitle={item?.episodeTitle}\n                    genreIds={item?.genreIds}\n                    genresAnime={item?.genresAnime}\n                    genresMovie={genresMovie}\n                    genresTv={genresTv}\n                    id={item?.id}\n                    isSliderCard\n                    job={item?.job}\n                    linkTo={href}\n                    knownFor={item?.knownFor}\n                    mediaType={item?.mediaType}\n                    overview={item?.overview}\n                    posterPath={item?.posterPath}\n                    releaseDate={item?.releaseDate}\n                    title={item?.title}\n                    trailer={item?.trailer}\n                    type={itemsType === 'episode' ? itemsType : 'card'}\n                    voteAverage={item?.voteAverage}\n                  />\n                </SwiperSlide>\n              );\n            })}\n        </Swiper>\n      ) : null}\n    </div>\n  );\n};\n\nexport default MediaListCard;\n"
  },
  {
    "path": "app/components/media/list/MediaListGrid.tsx",
    "content": "import { useEffect, useMemo, useRef, useState } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { Pagination } from '@nextui-org/pagination';\nimport { Spacer } from '@nextui-org/spacer';\nimport { useIntersectionObserver, useMediaQuery } from '@react-hookz/web';\nimport { useFetcher, useLocation, useSearchParams } from '@remix-run/react';\nimport { motion } from 'framer-motion';\nimport NProgress from 'nprogress';\nimport { useTranslation } from 'react-i18next';\nimport { tv } from 'tailwind-variants';\n\nimport type { IMedia } from '~/types/media';\nimport type { IAnimeList } from '~/services/consumet/anilist/anilist.types';\nimport { useGlobalLoadingState } from '~/utils/react/hooks/useGlobalNavigationState';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport { useLayout } from '~/store/layout/useLayout';\nimport Arrow from '~/assets/icons/ArrowIcon';\n\nimport MediaItem from '../item';\n\ninterface IMediaListCardProps {\n  coverItem?: { id: number; name: string; backdropPath: string }[];\n  currentPage?: number;\n  genresMovie?: { [id: string]: string };\n  genresTv?: { [id: string]: string };\n  hasNextPage?: boolean;\n  isCoverCard?: boolean;\n  isCreditsCard?: boolean;\n  items?: IMedia[];\n  itemsType?: 'movie' | 'tv' | 'anime' | 'people' | 'episode' | 'movie-tv';\n  listType?: 'table' | 'slider-card' | 'slider-banner' | 'grid';\n  provider?: string;\n  scrollToTopListAfterChangePage?: boolean;\n  totalPages?: number;\n}\n\ntype AnimeListItems = Omit<IAnimeList, 'results'> & { results: IMedia[] };\n\nconst mediaListGridStyles = tv({\n  base: 'grid w-full max-w-screen-4xl items-stretch justify-items-center gap-5',\n  variants: {\n    listViewType: {\n      table: 'grid-cols-1',\n      card: 'grid-cols-1 2xs:grid-cols-2 md:grid-cols-3 2xl:grid-cols-4 3xl:grid-cols-5 4xl:grid-cols-6',\n      detail: 'grid-cols-1 xl:grid-cols-2 4xl:grid-cols-3',\n      coverCard: 'grid-cols-1 xl:grid-cols-2 4xl:grid-cols-3',\n    },\n  },\n  defaultVariants: {\n    listViewType: 'card',\n  },\n});\n\nconst MediaListGrid = (props: IMediaListCardProps) => {\n  const {\n    coverItem,\n    currentPage,\n    genresMovie,\n    genresTv,\n    hasNextPage,\n    isCoverCard,\n    isCreditsCard,\n    items,\n    itemsType,\n    listType,\n    provider,\n    scrollToTopListAfterChangePage = false,\n    totalPages,\n  } = props;\n  const { t } = useTranslation();\n  const fetcher = useFetcher();\n  const location = useLocation();\n  const globalState = useGlobalLoadingState();\n  const [searchParams, setSearchParams] = useSearchParams({});\n  const { viewportRef } = useLayout((state) => state);\n  const [listItems, setListItems] = useState<IMedia[]>(items || []);\n  const [shouldFetch, setShouldFetch] = useState(false);\n  const [showLoadMore, setShowLoadMore] = useState(true);\n  const [page, setPage] = useState(2);\n  const topRef = useRef<HTMLDivElement>(null);\n  const bottomRef = useRef<HTMLDivElement>(null);\n  const { listLoadingType, listViewType, isShowTopPagination } = useSoraSettings();\n  const is2Xs = useMediaQuery('(max-width: 320px)', { initializeWithValue: false });\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const currentSearchParams = useMemo<{ [key: string]: string }>(() => {\n    const params: { [key: string]: string } = {};\n    searchParams.forEach((value, key) => {\n      params[key] = value;\n    });\n    return params;\n  }, [searchParams]);\n  const bottomIntersection = useIntersectionObserver(bottomRef, {\n    root: viewportRef,\n    rootMargin: '0px 0px 300px 0px',\n    threshold: [0],\n  });\n\n  useEffect(() => {\n    setListItems(items || []);\n    setPage(2);\n    setShouldFetch(false);\n    setShowLoadMore(true);\n  }, [items]);\n\n  useEffect(() => {\n    if (!bottomIntersection || !shouldFetch) return;\n    if (bottomIntersection.isIntersecting) {\n      fetcher.load(\n        `${location.pathname}${location.search || ''}${\n          location.search?.includes('?') ? '&' : '?'\n        }page=${page}${provider ? `&provider=${provider}` : ''}`,\n      );\n      setShouldFetch(false);\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [bottomIntersection]);\n\n  // Merge items, increment page, and allow fetching again\n  useEffect(() => {\n    // Discontinue API calls if the last page has been reached\n    if (fetcher.data && (fetcher.data as { length: number }).length === 0) {\n      setShouldFetch(false);\n      return;\n    }\n\n    // Items contain data, merge them and allow the possibility of another fetch\n    if (fetcher.data) {\n      if ((fetcher.data as { items: AnimeListItems }).items) {\n        setListItems((prevItems) => [\n          ...prevItems,\n          ...(fetcher.data as unknown as { items: AnimeListItems }).items.results,\n        ]);\n        if ((fetcher.data as { items: IAnimeList }).items.hasNextPage === true) {\n          setPage(page + 1);\n          setShouldFetch(true);\n        } else {\n          setShouldFetch(false);\n        }\n      } else if ((fetcher.data as { searchResults: AnimeListItems }).searchResults) {\n        setListItems((prevItems) => [\n          ...prevItems,\n          ...(fetcher.data as unknown as { searchResults: AnimeListItems }).searchResults.results,\n        ]);\n        if (\n          (fetcher.data as { searchResults: AnimeListItems }).searchResults.hasNextPage === true\n        ) {\n          setPage(page + 1);\n          setShouldFetch(true);\n        } else {\n          setShouldFetch(false);\n        }\n      } else if (Object.keys(fetcher.data).length) {\n        const newItems = Object.values(fetcher.data)[0] as {\n          items: IMedia[];\n          totalPages: number;\n          page: number;\n        };\n        if (newItems.items) {\n          setListItems((prevItems) => [...prevItems, ...newItems.items]);\n          if (newItems.page < newItems.totalPages) {\n            setPage(page + 1);\n            setShouldFetch(true);\n          } else {\n            setShouldFetch(false);\n          }\n        }\n      }\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [fetcher.data]);\n\n  useEffect(() => {\n    if (globalState === 'loading') {\n      NProgress.configure({ showSpinner: false }).start();\n    }\n    if (globalState === 'idle') {\n      NProgress.configure({ showSpinner: false }).done();\n    }\n  }, [globalState]);\n\n  const handlePageChange = ({\n    direction,\n    page,\n  }: {\n    direction?: 'next' | 'prev';\n    page?: number;\n  }) => {\n    if (direction) {\n      if (direction === 'prev' && currentPage && currentPage > 1) {\n        setSearchParams({ ...currentSearchParams, page: (currentPage - 1).toString() });\n      } else if (direction === 'next' && currentPage && hasNextPage) {\n        setSearchParams({ ...currentSearchParams, page: (currentPage + 1).toString() });\n      }\n    }\n    if (page) {\n      setSearchParams({\n        ...currentSearchParams,\n        page: page.toString(),\n      });\n    }\n    if (scrollToTopListAfterChangePage) {\n      topRef.current?.scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' });\n    }\n  };\n\n  const pagination =\n    listType === 'grid' && listLoadingType.value === 'pagination' ? (\n      itemsType === 'anime' || itemsType === 'episode' ? (\n        <>\n          <div className=\"flex flex-row gap-x-3\">\n            <Button\n              color=\"primary\"\n              isIconOnly\n              onPress={() => handlePageChange({ direction: 'prev' })}\n              isDisabled={currentPage === 1}\n            >\n              <Arrow direction=\"left\" />\n            </Button>\n            <Button\n              color=\"primary\"\n              isIconOnly\n              onPress={() => handlePageChange({ direction: 'next' })}\n              isDisabled={!hasNextPage}\n            >\n              <Arrow direction=\"right\" />\n            </Button>\n          </div>\n          <Spacer y={2.5} />\n        </>\n      ) : totalPages && totalPages > 1 ? (\n        <>\n          <Pagination\n            // showControls={!isSm}\n            total={totalPages}\n            initialPage={currentPage}\n            // shadow\n            onChange={(page) => handlePageChange({ page })}\n            {...(isSm && !is2Xs ? { size: 'md' } : isSm && is2Xs ? { size: 'sm' } : {})}\n          />\n          <Spacer y={2.5} />\n        </>\n      ) : null\n    ) : null;\n\n  if (isCoverCard) {\n    return (\n      <div className={mediaListGridStyles({ listViewType: 'coverCard' })}>\n        {coverItem &&\n          coverItem?.length > 0 &&\n          coverItem.map((item, index) => {\n            const href = `/lists/${item.id}`;\n            return (\n              <motion.div\n                key={`${item.id}-${index}-covercard-grid`}\n                initial={{ opacity: 0, scale: 0 }}\n                animate={{ opacity: 1, scale: 1 }}\n                transition={{ duration: 0.05 * index }}\n              >\n                <MediaItem\n                  backdropPath={item?.backdropPath}\n                  isCoverCard={isCoverCard}\n                  title={item?.name}\n                  type=\"card\"\n                  linkTo={href}\n                />\n              </motion.div>\n            );\n          })}\n      </div>\n    );\n  }\n  return (\n    <>\n      <div ref={topRef} />\n      {isShowTopPagination.value ? pagination : null}\n      {listItems && listItems?.length > 0 ? (\n        <div\n          className={mediaListGridStyles({\n            listViewType:\n              itemsType === 'episode' || itemsType === 'people'\n                ? 'card'\n                : isCreditsCard\n                ? 'table'\n                : listViewType.value,\n          })}\n        >\n          {listItems.map((item, index) => {\n            const href =\n              itemsType && itemsType === 'episode'\n                ? `/anime/${item.id}/episode/${item.episodeNumber}/watch?provider=${provider}`\n                : itemsType === 'anime'\n                ? `/anime/${item.id}/`\n                : itemsType === 'people'\n                ? `/people/${item.id}/`\n                : itemsType === 'movie'\n                ? `/movies/${item.id}/`\n                : itemsType === 'tv'\n                ? `/tv-shows/${item.id}/`\n                : itemsType === 'movie-tv' && item?.mediaType === 'movie'\n                ? `/movies/${item.id}/`\n                : itemsType === 'movie-tv' && item?.mediaType === 'tv'\n                ? `/tv-shows/${item.id}/`\n                : '/';\n            return (\n              <motion.div\n                key={`${item.id}-${index}-card-grid`}\n                initial={{ opacity: 0, scale: 0 }}\n                animate={{ opacity: 1, scale: 1 }}\n                transition={\n                  listLoadingType.value === 'infinite-scroll'\n                    ? { x: { type: 'spring', stiffness: 100 }, duration: 0.1 }\n                    : { duration: 0.05 * index }\n                }\n                className={\n                  isCreditsCard\n                    ? 'w-full'\n                    : listViewType.value === 'table' &&\n                      itemsType !== 'episode' &&\n                      itemsType !== 'people'\n                    ? 'w-full'\n                    : listViewType.value === 'detail' &&\n                      itemsType !== 'episode' &&\n                      itemsType !== 'people'\n                    ? 'w-full sm:w-fit'\n                    : ''\n                }\n              >\n                <MediaItem\n                  backdropPath={item?.backdropPath}\n                  character={item?.character}\n                  color={item?.color}\n                  episodeNumber={item?.episodeNumber}\n                  episodeTitle={item?.episodeTitle}\n                  genreIds={item?.genreIds}\n                  genresAnime={item?.genresAnime}\n                  genresMovie={genresMovie}\n                  genresTv={genresTv}\n                  id={item?.id}\n                  job={item?.job}\n                  key={item.id}\n                  knownFor={item?.knownFor}\n                  linkTo={href}\n                  mediaType={item?.mediaType}\n                  overview={item?.overview}\n                  posterPath={item?.posterPath}\n                  releaseDate={item?.releaseDate}\n                  title={item?.title}\n                  trailer={item?.trailer}\n                  type={itemsType === 'episode' ? itemsType : 'card'}\n                  voteAverage={item?.voteAverage}\n                  isCreditsCard={isCreditsCard}\n                />\n              </motion.div>\n            );\n          })}\n        </div>\n      ) : (\n        <div className=\"flex w-full items-center justify-center\">\n          <h4 className=\"opacity-70\">{t('no-results')}</h4>\n        </div>\n      )}\n      <Spacer y={5} />\n      {!shouldFetch &&\n      (hasNextPage || (currentPage && totalPages && currentPage < totalPages)) &&\n      showLoadMore &&\n      listLoadingType.value === 'infinite-scroll' ? (\n        <Button\n          type=\"button\"\n          // shadow\n          fullWidth\n          color=\"primary\"\n          onPress={() => {\n            fetcher.load(\n              `${location.pathname}${location.search || ''}${\n                location.search?.includes('?') ? '&' : '?'\n              }page=${page}${provider ? `&provider=${provider}` : ''}`,\n            );\n            setShowLoadMore(false);\n          }}\n        >\n          {t('load-more')}\n        </Button>\n      ) : null}\n      {pagination}\n      <div ref={bottomRef} />\n    </>\n  );\n};\n\nexport default MediaListGrid;\n"
  },
  {
    "path": "app/components/media/list/index.tsx",
    "content": "export { default as MediaListBanner } from './MediaListBanner';\nexport { default as MediaListCard } from './MediaListCard';\nexport { default as MediaListGrid } from './MediaListGrid';\n"
  },
  {
    "path": "app/components/styles/nprogress.css",
    "content": "/* Make clicks pass-through */\n#nprogress {\n  pointer-events: none;\n}\n\n#nprogress .bar {\n  position: fixed;\n  z-index: 9999;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 4px;\n  background: #27c4f5 linear-gradient(to right, #27c4f5, #a307ba, #fd8d32, #70c050, #27c4f5);\n  background-size: 500%;\n  animation: 2s linear infinite barprogress, 0.3s fadein;\n}\n\n/* Fancy blur effect */\n#nprogress .peg {\n  display: block;\n  position: absolute;\n  right: 0;\n  width: 100px;\n  height: 100%;\n  box-shadow: 0 0 10px #29d, 0 0 5px #29d;\n  opacity: 1;\n  -webkit-transform: rotate(3deg) translate(0, -4px);\n  -ms-transform: rotate(3deg) translate(0, -4px);\n  transform: rotate(3deg) translate(0, -4px);\n}\n\n.nprogress-custom-parent {\n  overflow: hidden;\n  position: relative;\n}\n\n.nprogress-custom-parent #nprogress .spinner,\n.nprogress-custom-parent #nprogress .bar {\n  position: absolute;\n}\n\n@keyframes barprogress {\n  0% {\n    background-position: 0% 0;\n  }\n\n  to {\n    background-position: 125% 0;\n  }\n}\n"
  },
  {
    "path": "app/components/styles/primitives.ts",
    "content": "import { tv } from 'tailwind-variants';\n\nexport const backgroundStyles = tv({\n  base: 'absolute top-0 h-full w-full',\n  variants: {\n    tablink: {\n      true: 'z-[1] bg-gradient-to-b from-background/20 to-background',\n    },\n    content: {\n      true: 'z-[0] bg-gradient-to-b from-transparent to-background/20',\n    },\n  },\n});\n"
  },
  {
    "path": "app/constants/episodeTypes.ts",
    "content": "const episodeTypes = [\n  {\n    activeType: 0,\n    activeTypeName: 'Number',\n  },\n  {\n    activeType: 1,\n    activeTypeName: 'Image',\n  },\n];\n\nexport default episodeTypes;\n"
  },
  {
    "path": "app/constants/featuredList.ts",
    "content": "const featuredList = [\n  {\n    id: 146567,\n    name: 'Anime Series',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/1Ep6YHL5QcrNC1JN6RYalWRPopi.jpg',\n  },\n  {\n    id: 1,\n    name: 'The Marvel Universe',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/kaIfm5ryEOwYg8mLbq8HkPuM1Fo.jpg',\n  },\n  {\n    id: 146572,\n    name: 'Korean Drama & Movies',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/9iypSe1gvjExzZJL0JNR80x5OTS.jpg',\n  },\n  {\n    id: 3,\n    name: 'The DC Universe',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/m6MYSifkYciC4hpjG96zBe39cHR.jpg',\n  },\n  {\n    id: 3945,\n    name: 'Good Science Fiction Flicks',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/mZSAu5acXueGC4Z3S5iLSWx8AEp.jpg',\n  },\n  {\n    id: 3321,\n    name: 'Anime Movies',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/shqLeIkqPAAXM8iT6wVDiXUYz1p.jpg',\n  },\n  {\n    id: 43372,\n    name: 'Golden Globe Winners',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/8wYluVq5AVXhnFFMhUYD9D4rTz5.jpg',\n  },\n  {\n    id: 3700,\n    name: 'Films by Pixar Animation Studios',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/lxD5ak7BOoinRNehOCA85CQ8ubr.jpg',\n  },\n  {\n    id: 28,\n    name: 'Best Picture Winners - The Academy Awards',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/rgyhSn3mINvkuy9iswZK0VLqQO3.jpg',\n  },\n  {\n    id: 83082,\n    name: 'Mafia & Gangster',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/rSPw7tgCH9c6NqICZef4kZjFOQ5.jpg',\n  },\n  {\n    id: 43,\n    name: \"AFI's 100 Most Thrilling American Films\",\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/xcjJ5khg2yzOa282mza39Lbrm7j.jpg',\n  },\n  {\n    id: 82976,\n    name: 'Spy Movies',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/eR20N1flPCQyp9HzpxlTcxgDAO7.jpg',\n  },\n  {\n    id: 83081,\n    name: 'Magical',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/c7U9Fuy74WLp7gFAdpQJHn2T2no.jpg',\n  },\n  {\n    id: 82963,\n    name: 'Time Travel',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/auZIuHEUec5tBTns3tCRXfayxZq.jpg',\n  },\n  {\n    id: 70091,\n    name: 'Emmys: Outstanding Comedy Series Winners',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/wImNeqxKsqmJ5OBw8j3I37GNFN3.jpg',\n  },\n  {\n    id: 68104,\n    name: 'Emmys: Outstanding Drama Series Winners',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/4InrdamBEM31unNiuEHGYTPX1e2.jpg',\n  },\n  {\n    id: 132862,\n    name: 'Creepy Series',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/ssyZgYScV9eClakZDwu4sPrgMmy.jpg',\n  },\n  {\n    id: 37375,\n    name: 'Good RomCom Flicks',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/gyWE2qIMux06FTgA06CV97ngbEM.jpg',\n  },\n  {\n    id: 132861,\n    name: 'Fantasy Series',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/kysKBF2CJG9qfQDSCDaboJrkZy1.jpg',\n  },\n  {\n    id: 338,\n    name: 'Disney Classic Collection',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/jsgRkhPxYtzAhDFCUyNbvlX63tY.jpg',\n  },\n  {\n    id: 132860,\n    name: 'Golden Globe Winners 2020',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/2WgieNR1tGHlpJUsolbVzbUbE1O.jpg',\n  },\n  {\n    id: 1131,\n    name: 'Greatest Twist Ending',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/2nqsOT2AqPkTW81bWaLRtjgjqVM.jpg',\n  },\n  {\n    id: 10,\n    name: 'Top 50 Grossing Films of All Time (Worldwide)',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/rzdPqYx7Um4FUZeD8wpXqjAUcEm.jpg',\n  },\n  {\n    id: 132859,\n    name: 'Top 10 Shows 2019 (TMDb)',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/uL6Ad12W09L1sfuOE2pcTeak7bt.jpg',\n  },\n  {\n    id: 132857,\n    name: 'Top 10 Movies 2019 (TMDb)',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/7RyHsO4yDXtBv1zUU3mTpHeQ0d5.jpg',\n  },\n  {\n    id: 43923,\n    name: 'Netflix Top Picks',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/piuRhGiQBYWgW668eSNJ2ug5uAO.jpg',\n  },\n  {\n    id: 122440,\n    name: 'Emmy Awards 2019',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/900tHlUYUkp7Ol04XFSoAaEIXcT.jpg',\n  },\n  {\n    id: 118240,\n    name: 'Oscar 2019',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/xcaSYLBhmDzJ6P14bcKe0KTh3QV.jpg',\n  },\n  {\n    id: 112870,\n    name: 'Cannes 2019',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/cXyfAViYly0Lk2CVpEKgYbt9wKQ.jpg',\n  },\n  {\n    id: 43921,\n    name: 'Netflix Originals - TV Shows',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/56v2KjBlU4XaOv9rVYEQypROD7P.jpg',\n  },\n  {\n    id: 43893,\n    name: 'Netflix Originals - Movies',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/6V5L5CN3zRf9KLeEq6B4Hy6xr0A.jpg',\n  },\n  {\n    id: 82960,\n    name: 'Zombies',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/xVzvD5BPAU4HpleFSo8QOdHkndo.jpg',\n  },\n  {\n    id: 82949,\n    name: 'Best Samurai Movies',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/xaSQ4Ngs0mhYPS25c2a5MHDf883.jpg',\n  },\n  {\n    id: 63978,\n    name: 'Oscars 2018',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/rgyhSn3mINvkuy9iswZK0VLqQO3.jpg',\n  },\n  {\n    id: 118239,\n    name: 'Top 10 Movies 2018 (TMDb)',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/bOGkgRGdhrBYJSLpXaxhXVstddV.jpg',\n  },\n  {\n    id: 17445,\n    name: '2017 Oscar Nominations for Best Picture - 89th Academy Awards',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/A9KPbYTQvWsp51Lgz85ukVkFrKf.jpg',\n  },\n  {\n    id: 44677,\n    name: 'TOP 10 Movies 2017 (TMDb)',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/adFgBkl35kdI0bSrWZO2xury1JB.jpg',\n  },\n  {\n    id: 44676,\n    name: 'TOP 10 TV Shows 2017 (TMDb)',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/oFvjxSIPYv5YXspDlnWeDJhnPHI.jpg',\n  },\n  {\n    id: 24335,\n    name: 'Cannes 2017 Complete Lineup',\n    backdropPath:\n      'https://www.themoviedb.org/t/p/w500_and_h282_multi_faces/velMcFG0eIv0MBARstwM42j8UVj.jpg',\n  },\n];\n\nexport default featuredList;\n"
  },
  {
    "path": "app/constants/filterItems.ts",
    "content": "export const sortMovieTvItems = [\n  'popularity.desc',\n  'popularity.asc',\n  'primary_release_date.desc',\n  'primary_release_date.asc',\n  'title.asc',\n  'title.desc',\n  'vote_average.desc',\n  'vote_average.asc',\n];\n\nexport const tvStatus: { [id: string]: string } = {\n  0: 'Returning Series',\n  1: 'Planned',\n  2: 'In Production',\n  3: 'Ended',\n  4: 'Canceled',\n  5: 'Pilot',\n};\n\n// export const tvType: { [id: string]: string } = {\n//   0: 'Documentary',\n//   1: 'News',\n//   2: 'Miniseries',\n//   3: 'Reality',\n//   4: 'Scripted',\n//   5: 'Talk Show',\n//   6: 'Video',\n// };\n\nexport const animeGenres: string[] = [\n  'Action',\n  'Adventure',\n  'Cars',\n  'Comedy',\n  'Drama',\n  'Fantasy',\n  'Horror',\n  'Mahou Shoujo',\n  'Mecha',\n  'Music',\n  'Mystery',\n  'Psychological',\n  'Romance',\n  'Sci-Fi',\n  'Slice of Life',\n  'Sports',\n  'Supernatural',\n  'Thriller',\n];\n\nexport const animeFormat: string[] = ['MOVIE', 'MUSIC', 'ONA', 'OVA', 'SPECIAL', 'TV_SHORT', 'TV'];\n\nexport const animeSort: string[] = [\n  'title_english',\n  'title_romaji',\n  'popularity_desc',\n  'score_desc',\n  'trending_desc',\n  'favourites_desc',\n  'start_date_desc',\n];\n\nexport const animeSeason = ['FALL', 'SPRING', 'SUMMER', 'WINTER'];\n\nexport const animeStatus = ['CANCELLED', 'FINISHED', 'HIATUS', 'NOT_YET_RELEASED', 'RELEASING'];\n"
  },
  {
    "path": "app/constants/languages.ts",
    "content": "const languages: string[] = ['en', 'fr', 'vi'];\n\nexport default languages;\n"
  },
  {
    "path": "app/constants/settings.ts",
    "content": "const settingsTab = [\n  {\n    id: 'general-tab',\n    title: 'general',\n    disabled: false,\n  },\n  {\n    id: 'appearance-tab',\n    title: 'appearance',\n    disabled: false,\n  },\n  // {\n  //   id: 'account-tab',\n  //   title: 'account',\n  //   disabled: true,\n  // },\n  {\n    id: 'player-tab',\n    title: 'player',\n    disabled: false,\n  },\n  {\n    id: 'about-tab',\n    title: 'about',\n    disabled: false,\n  },\n];\n\nconst listThemes = [\n  'light',\n  'dark',\n  'light-red',\n  'light-yellow',\n  'light-green',\n  'light-cyan',\n  'light-purple',\n  'light-pink',\n  'dark-red',\n  'dark-yellow',\n  'dark-green',\n  'dark-cyan',\n  'dark-purple',\n  'dark-pink',\n  'cupcake',\n  'bumblebee',\n  'emerald',\n  'corporate',\n  'synthwave',\n  'retro',\n  'cyberpunk',\n  'valentine',\n  'halloween',\n  'garden',\n  'forest',\n  'aqua',\n  'lofi',\n  'pastel',\n  'fantasy',\n  'wireframe',\n  'luxury',\n  'dracula',\n  'cmyk',\n  'autumn',\n  'business',\n  'acid',\n  'lemonade',\n  'night',\n  'coffee',\n  'winter',\n];\n\nconst listSubtitleFontColor = [\n  'White',\n  'Blue',\n  'Purple',\n  'Green',\n  'Yellow',\n  'Red',\n  'Cyan',\n  'Pink',\n  'Black',\n];\n\nconst listSubtitleFontSize = ['50%', '75%', '100%', '125%', '150%', '175%', '200%', '300%'];\n\nconst listSubtitleBackgroundColor = [\n  'White',\n  'Blue',\n  'Purple',\n  'Green',\n  'Yellow',\n  'Red',\n  'Cyan',\n  'Pink',\n  'Black',\n];\n\nconst listSubtitleBackgroundOpacity = ['0%', '25%', '50%', '75%', '100%'];\n\nconst listSubtitleWindowColor = [\n  'White',\n  'Blue',\n  'Purple',\n  'Green',\n  'Yellow',\n  'Red',\n  'Cyan',\n  'Pink',\n  'Black',\n];\n\nconst listSubtitleWindowOpacity = ['0%', '25%', '50%', '75%', '100%'];\n\nconst listSubtitleTextEffects = ['None', 'Drop Shadow', 'Raised', 'Depressed', 'Outline'];\n\nconst listSidebarActiveStyleMode = ['rounded-all', 'rounded-one-side', 'pill-all', 'pill-one-side'];\nconst listListViewType = ['card', 'detail', 'table'];\nconst listListLoadingType = ['pagination', 'infinite-scroll'];\nconst listDefaultThemeColors = ['red', 'yellow', 'green', 'cyan', 'purple', 'pink', 'blue'];\nconst listCustomThemeColors = [\n  'cupcake',\n  'bumblebee',\n  'emerald',\n  'corporate',\n  'synthwave',\n  'retro',\n  'cyberpunk',\n  'valentine',\n  'halloween',\n  'garden',\n  'forest',\n  'aqua',\n  'lofi',\n  'pastel',\n  'fantasy',\n  'wireframe',\n  'luxury',\n  'dracula',\n  'cmyk',\n  'autumn',\n  'business',\n  'acid',\n  'lemonade',\n  'night',\n  'coffee',\n  'winter',\n];\n\nexport {\n  settingsTab,\n  listThemes,\n  listSubtitleFontColor,\n  listSubtitleFontSize,\n  listSubtitleBackgroundColor,\n  listSubtitleBackgroundOpacity,\n  listSubtitleWindowColor,\n  listSubtitleWindowOpacity,\n  listSubtitleTextEffects,\n  listSidebarActiveStyleMode,\n  listListViewType,\n  listListLoadingType,\n  listDefaultThemeColors,\n  listCustomThemeColors,\n};\n"
  },
  {
    "path": "app/constants/tabLinks.ts",
    "content": "export const moviePages = [\n  { pageName: 'popular-movies', pageLink: '/popular' },\n  { pageName: 'now-playing-movies', pageLink: '/now-playing' },\n  { pageName: 'upcoming-movies', pageLink: '/upcoming' },\n  { pageName: 'top-rated-movies', pageLink: '/top-rated' },\n];\n\nexport const tvPages = [\n  { pageName: 'popular-tv-shows', pageLink: '/popular' },\n  { pageName: 'airing-today-tv-shows', pageLink: '/airing-today' },\n  { pageName: 'on-the-air-tv-shows', pageLink: '/on-the-air' },\n  { pageName: 'top-rated-tv-shows', pageLink: '/top-rated' },\n];\n\nexport const animePages = [\n  { pageName: 'popular-anime', pageLink: '/popular' },\n  { pageName: 'trending-anime', pageLink: '/trending' },\n  { pageName: 'recent-episodes', pageLink: '/recent-episodes' },\n];\n\nexport const discoverPages = [\n  { pageName: 'discover-movies', pageLink: '/movies' },\n  { pageName: 'discover-tv', pageLink: '/tv-shows' },\n  { pageName: 'discover-anime', pageLink: '/anime' },\n];\n\nexport const searchPages = [\n  { pageName: 'search.title.movie', pageLink: 'movie' },\n  { pageName: 'search.title.tv', pageLink: 'tv' },\n  { pageName: 'search.title.anime', pageLink: 'anime' },\n  { pageName: 'search.title.people', pageLink: 'people' },\n];\n\nexport const genrePages = [\n  { pageName: 'movie-genres', pageLink: '/movie' },\n  { pageName: 'tv-show-genres', pageLink: '/tv' },\n  { pageName: 'anime-genres', pageLink: '/anime' },\n];\n\nexport const movieTvDetailsPages = [\n  { pageName: 'overview', pageLink: '/' },\n  { pageName: 'cast', pageLink: '/cast' },\n  { pageName: 'crew', pageLink: '/crew' },\n  { pageName: 'videos', pageLink: '/videos' },\n  { pageName: 'photos', pageLink: '/photos' },\n  { pageName: 'recommendations', pageLink: '/recommendations' },\n  { pageName: 'similar', pageLink: '/similar' },\n];\n\nexport const animeDetailsPages = [\n  { pageName: 'overview', pageLink: '/' },\n  { pageName: 'episodes', pageLink: '/episodes' },\n  { pageName: 'character', pageLink: '/characters' },\n  { pageName: 'staff', pageLink: '/staff' },\n];\n\nexport const peopleDetailPages = [\n  { pageName: 'overview', pageLink: '/' },\n  { pageName: 'credits', pageLink: '/credits' },\n  { pageName: 'media', pageLink: '/media' },\n];\n\nexport const tvSeasonDetailPages = [\n  { pageName: 'episodes', pageLink: '/' },\n  { pageName: 'cast', pageLink: '/cast' },\n  { pageName: 'crew', pageLink: '/crew' },\n  { pageName: 'videos', pageLink: '/videos' },\n  { pageName: 'photos', pageLink: '/photos' },\n];\n\nexport const trendingPages = {\n  all: [\n    { pageName: 'trending.all.day', pageLink: '/today' },\n    { pageName: 'trending.all.week', pageLink: '/week' },\n  ],\n  movie: [\n    { pageName: 'trending.movie.day', pageLink: '/today' },\n    { pageName: 'trending.movie.week', pageLink: '/week' },\n  ],\n  tv: [\n    { pageName: 'trending.tv.day', pageLink: '/today' },\n    { pageName: 'trending.tv.week', pageLink: '/week' },\n  ],\n  people: [\n    { pageName: 'trending.people.day', pageLink: '/today' },\n    { pageName: 'trending.people.week', pageLink: '/week' },\n  ],\n};\n\nexport const designSystemPages = [\n  { pageName: 'Colors', pageLink: '/colors' },\n  { pageName: 'Typography', pageLink: '/typography' },\n  { pageName: 'Icons', pageLink: '/icons' },\n  { pageName: 'Avatar', pageLink: '/avatar' },\n  { pageName: 'Accordion', pageLink: '/accordion' },\n  { pageName: 'Badge', pageLink: '/badge' },\n  { pageName: 'Button', pageLink: '/button' },\n  { pageName: 'Card', pageLink: '/card' },\n  { pageName: 'Dialog', pageLink: '/dialog' },\n  { pageName: 'Divider', pageLink: '/divider' },\n  { pageName: 'Image', pageLink: '/image' },\n  { pageName: 'Input', pageLink: '/input' },\n  { pageName: 'Pagination', pageLink: '/pagination' },\n  { pageName: 'Popover', pageLink: '/popover' },\n  { pageName: 'Progress', pageLink: '/progress' },\n  { pageName: 'Scroll Area', pageLink: '/scroll-area' },\n  { pageName: 'Select', pageLink: '/select' },\n  { pageName: 'Skeleton', pageLink: '/skeleton' },\n  { pageName: 'Slider', pageLink: '/slider' },\n  { pageName: 'Switch', pageLink: '/switch' },\n  { pageName: 'Tabs', pageLink: '/tabs' },\n  { pageName: 'Toast', pageLink: '/toast' },\n  { pageName: 'Tooltip', pageLink: '/tooltip' },\n  { pageName: 'Video player', pageLink: '/video-player' },\n];\n"
  },
  {
    "path": "app/context/isbot.context.tsx",
    "content": "import { createContext, useContext, type ReactNode } from 'react';\n\ntype Props = { isBot: boolean; children: ReactNode };\n\nconst IsbotContext = createContext(false);\n\nconst useIsBot = () => useContext(IsbotContext) ?? false;\n\nconst IsBotProvider = (props: Props) => {\n  const { isBot, children } = props;\n  return <IsbotContext.Provider value={isBot}>{children}</IsbotContext.Provider>;\n};\n\nexport { useIsBot, IsBotProvider };\n"
  },
  {
    "path": "app/entry.client.tsx",
    "content": "import { startTransition } from 'react';\nimport { loadServiceWorker } from '@remix-pwa/sw';\nimport { RemixBrowser } from '@remix-run/react';\nimport i18next from 'i18next';\nimport LanguageDetector from 'i18next-browser-languagedetector';\nimport Backend from 'i18next-http-backend';\nimport { hydrateRoot } from 'react-dom/client';\nimport { I18nextProvider, initReactI18next } from 'react-i18next';\nimport { getInitialNamespaces } from 'remix-i18next';\n\nimport { i18n } from '~/services/i18n';\n\ni18next\n  .use(initReactI18next) // Tell i18next to use the react-i18next plugin\n  .use(LanguageDetector) // Setup a client-side language detector\n  .use(Backend) // Setup your backend\n  .init({\n    ...i18n, // spread the configuration\n    // This function detects the namespaces your routes rendered while SSR use\n    ns: getInitialNamespaces(),\n    backend: {\n      loadPath: '/locales/{{lng}}/{{ns}}.json',\n    },\n    detection: {\n      // Here only enable htmlTag detection, we'll detect the language only\n      // server-side with remix-i18next, by using the `<html lang>` attribute\n      // we can communicate to the client the language detected server-side\n      order: ['htmlTag'],\n      // Because we only use htmlTag, there's no reason to cache the language\n      // on the browser, so we disable it\n      caches: [],\n    },\n  })\n  .then(() =>\n    startTransition(() => {\n      // After i18next has been initialized, we can hydrate the app\n      // We need to wait to ensure translations are loaded before the hydration\n      // Here wrap RemixBrowser in I18nextProvider from react-i18next\n      hydrateRoot(\n        document,\n        <I18nextProvider i18n={i18next}>\n          <RemixBrowser />\n        </I18nextProvider>,\n      );\n    }),\n  )\n  .catch((err) => {\n    // eslint-disable-next-line no-console\n    console.error(err);\n  });\n\nloadServiceWorker({\n  serviceWorkerUrl: `/sw.js${\n    window.process.env.NODE_ENV === 'production'\n      ? `?version=${window.process.env.VERCEL_GIT_COMMIT_SHA}`\n      : ''\n  }`,\n});\n"
  },
  {
    "path": "app/entry.server.tsx",
    "content": "import { resolve } from 'node:path';\nimport { RemixServer } from '@remix-run/react';\nimport { handleRequest as handleVercelRequest, type EntryContext } from '@vercel/remix';\nimport { createInstance } from 'i18next';\nimport Backend from 'i18next-fs-backend';\nimport isbot from 'isbot';\nimport { I18nextProvider, initReactI18next } from 'react-i18next';\n\nimport { IsBotProvider } from '~/context/isbot.context';\n\nimport { i18n, i18next } from './services/i18n';\n\nconst handleRequest = async (\n  request: Request,\n  responseStatusCode: number,\n  responseHeaders: Headers,\n  remixContext: EntryContext,\n) => {\n  // First, we create a new instance of i18next so every request will have a\n  // completely unique instance and not share any state\n  const instance = createInstance();\n\n  // Then we could detect locale from the request\n  const lng = await i18next.getLocale(request);\n  // And here we detect what namespaces the routes about to render want to use\n  const ns = i18next.getRouteNamespaces(remixContext);\n\n  await instance\n    .use(initReactI18next) // Tell our instance to use react-i18next\n    .use(Backend) // Setup our backend\n    .init({\n      ...i18n, // spread the configuration\n      lng, // The locale we detected above\n      ns, // The namespaces the routes about to render wants to use\n      backend: {\n        loadPath: resolve('./public/locales/{{lng}}/{{ns}}.json'),\n      },\n    });\n\n  const isbotRender = isbot.spawn();\n  isbotRender.exclude([\n    'Checkly',\n    'Checkly, https://www.checklyhq.com',\n    'Checkly/1.0 (https://www.checklyhq.com)',\n    'googlebot',\n    'googlebot/2.1 (+http://www.google.com/bot.html)',\n    'bingbot',\n    'bingbot/2.0 (+http://www.bing.com/bingbot.htm)',\n    'discordbot',\n    'Discordbot/2.0',\n    'twitterbot',\n    'Twitterbot/1.0',\n    'vercel',\n    'Vercel/1.0 (https://vercel.com/docs/bots)',\n  ]);\n  isbotRender.extend(['chrome-lighthouse']);\n  const remixServer = (\n    <IsBotProvider isBot={isbotRender(request.headers.get('User-Agent') ?? '')}>\n      <I18nextProvider i18n={instance}>\n        <RemixServer context={remixContext} url={request.url} />\n      </I18nextProvider>\n    </IsBotProvider>\n  );\n  return handleVercelRequest(request, responseStatusCode, responseHeaders, remixServer);\n};\n\nexport default handleRequest;\n"
  },
  {
    "path": "app/entry.worker.ts",
    "content": "/// <reference lib=\"WebWorker\" />\n\nimport { Storage } from '@remix-pwa/cache';\nimport { cacheFirst, networkFirst } from '@remix-pwa/strategy';\nimport {\n  // logger,\n  matchRequest,\n  RemixNavigationHandler,\n  type DefaultFetchHandler,\n} from '@remix-pwa/sw';\n\ndeclare let self: ServiceWorkerGlobalScope;\n\nconst PAGES = 'page-cache';\nconst DATA = 'data-cache';\nconst ASSETS = 'assets-cache';\n\n// Open the caches and wrap them in `RemixCache` instances.\nconst dataCache = Storage.open(DATA, {\n  ttl: 60 * 60 * 24 * 7 * 1_000, // 7 days\n});\nconst documentCache = Storage.open(PAGES);\nconst assetCache = Storage.open(ASSETS);\n\n// self.addEventListener('install', (event: ExtendableEvent) => {\n//   logger.log('Service worker installed');\n//   event.waitUntil(self.skipWaiting());\n// });\n\n// self.addEventListener('activate', (event: ExtendableEvent) => {\n//   logger.log('Service worker activated');\n//   event.waitUntil(self.clients.claim());\n// });\n\nconst dataHandler = networkFirst({\n  cache: dataCache,\n});\n\nconst assetsHandler = cacheFirst({\n  cache: assetCache,\n  cacheQueryOptions: {\n    ignoreSearch: true,\n    ignoreVary: true,\n  },\n});\n\n// The default fetch event handler will be invoke if the\n// route is not matched by any of the worker action/loader.\nexport const defaultFetchHandler: DefaultFetchHandler = ({ context, request }) => {\n  const type = matchRequest(request);\n\n  if (type === 'asset') {\n    return assetsHandler(context.event.request);\n  }\n\n  if (type === 'loader') {\n    return dataHandler(context.event.request);\n  }\n\n  return context.fetchFromServer();\n};\n\nconst handler = new RemixNavigationHandler({\n  dataCache,\n  documentCache,\n});\n\nself.addEventListener('message', (event) => {\n  if (event.data === 'skipWaiting') {\n    self.skipWaiting();\n  }\n  event.waitUntil(handler.handle(event));\n});\n"
  },
  {
    "path": "app/root.tsx",
    "content": "/* eslint-disable no-console */\nimport * as React from 'react';\nimport FontStyles100 from '@fontsource/sora/100.css';\nimport FontStyles200 from '@fontsource/sora/200.css';\nimport FontStyles300 from '@fontsource/sora/300.css';\nimport FontStyles400 from '@fontsource/sora/400.css';\nimport FontStyles500 from '@fontsource/sora/500.css';\nimport FontStyles600 from '@fontsource/sora/600.css';\nimport FontStyles700 from '@fontsource/sora/700.css';\nimport FontStyles800 from '@fontsource/sora/800.css';\nimport { Button } from '@nextui-org/button';\nimport { Image as NextUIImage } from '@nextui-org/image';\nimport { NextUIProvider as NextUIv2Provider } from '@nextui-org/system';\nimport { LiveReload, logger, useSWEffect } from '@remix-pwa/sw';\nimport { cssBundleHref } from '@remix-run/css-bundle';\nimport {\n  json,\n  type LinkDescriptor,\n  type LinksFunction,\n  type LoaderFunctionArgs,\n  type MetaFunction,\n} from '@remix-run/node';\nimport {\n  isRouteErrorResponse,\n  Links,\n  Meta,\n  Scripts,\n  useBeforeUnload,\n  useFetchers,\n  useLoaderData,\n  useLocation,\n  useNavigation,\n  useRouteError,\n} from '@remix-run/react';\nimport type { User } from '@supabase/supabase-js';\nimport { AnimatePresence, motion } from 'framer-motion';\nimport { ThemeProvider as RemixThemesProvider } from 'next-themes';\nimport NProgress from 'nprogress';\nimport photoSwipeStyles from 'photoswipe/dist/photoswipe.css';\nimport { getSelectorsByUserAgent } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\nimport { Provider as WrapBalancerProvider } from 'react-wrap-balancer';\nimport rdtStylesheet from 'remix-development-tools/index.css';\nimport { useChangeLanguage } from 'remix-i18next';\nimport Image, { MimeType } from 'remix-image';\nimport { getClientIPAddress } from 'remix-utils/get-client-ip-address';\nimport { getClientLocales } from 'remix-utils/locales/server';\nimport { toast } from 'sonner';\n// @ts-ignore\nimport swiperStyles from 'swiper/css';\n// @ts-ignore\nimport swiperAutoPlayStyles from 'swiper/css/autoplay';\n// @ts-ignore\nimport swiperFreeModeStyles from 'swiper/css/free-mode';\n// @ts-ignore\nimport swiperPaginationStyles from 'swiper/css/navigation';\n// @ts-ignore\nimport swiperNavigationStyles from 'swiper/css/pagination';\n// @ts-ignore\nimport swiperThumbsStyles from 'swiper/css/thumbs';\n\nimport Home from './assets/icons/HomeIcon';\nimport Refresh from './assets/icons/RefreshIcon';\nimport pageNotFound from './assets/images/404.gif';\nimport logoLoading from './assets/images/logo_loading.png';\nimport { BreadcrumbItem } from './components/elements/Breadcrumb';\nimport Layout from './components/layouts/Layout';\nimport nProgressStyles from './components/styles/nprogress.css';\nimport { listThemes } from './constants/settings';\nimport { useIsBot } from './context/isbot.context';\nimport { i18nCookie, i18next } from './services/i18n';\nimport { getUserFromCookie } from './services/supabase';\nimport { getListGenre, getListLanguages } from './services/tmdb/tmdb.server';\nimport tailwindStylesheetUrl from './styles/tailwind.css';\nimport type { Handle } from './types/handle';\nimport { combineHeaders } from './utils';\nimport * as gtag from './utils/client/gtags.client';\nimport { useHydrated } from './utils/react/hooks/useHydrated';\nimport { useToast } from './utils/react/hooks/useToast';\nimport { getToastSession } from './utils/server/toast-session.server';\n\ninterface DocumentProps {\n  children: React.ReactNode;\n  title?: string;\n}\n\nconst themeValues = {\n  light: 'light',\n  dark: 'dark',\n  'light-red': 'light-red',\n  'light-yellow': 'light-yellow',\n  'light-green': 'light-green',\n  'light-cyan': 'light-cyan',\n  'light-purple': 'light-purple',\n  'light-pink': 'light-pink',\n  'dark-red': 'dark-red',\n  'dark-yellow': 'dark-yellow',\n  'dark-green': 'dark-green',\n  'dark-cyan': 'dark-cyan',\n  'dark-purple': 'dark-purple',\n  'dark-pink': 'dark-pink',\n  cupcake: 'cupcake',\n  bumblebee: 'bumblebee',\n  emerald: 'emerald',\n  corporate: 'corporate',\n  synthwave: 'synthwave',\n  retro: 'retro',\n  cyberpunk: 'cyberpunk',\n  valentine: 'valentine',\n  halloween: 'halloween',\n  garden: 'garden',\n  forest: 'forest',\n  aqua: 'aqua',\n  lofi: 'lofi',\n  pastel: 'pastel',\n  fantasy: 'fantasy',\n  wireframe: 'wireframe',\n  luxury: 'luxury',\n  dracula: 'dracula',\n  cmyk: 'cmyk',\n  autumn: 'autumn',\n  business: 'business',\n  acid: 'acid',\n  lemonade: 'lemonade',\n  night: 'night',\n  coffee: 'coffee',\n  winter: 'winter',\n};\n\nexport const links: LinksFunction = () => {\n  return [\n    { rel: 'manifest', href: '/manifest.webmanifest' },\n    { rel: 'icon', href: '/favicon.ico', type: 'image/x-icon' },\n    // Preload CSS as a resource to avoid render blocking\n    { rel: 'preload', as: 'style', href: tailwindStylesheetUrl },\n    cssBundleHref ? { rel: 'preload', as: 'style', href: cssBundleHref } : null,\n    { rel: 'preload', as: 'style', href: FontStyles100 },\n    { rel: 'preload', as: 'style', href: FontStyles200 },\n    { rel: 'preload', as: 'style', href: FontStyles300 },\n    { rel: 'preload', as: 'style', href: FontStyles400 },\n    { rel: 'preload', as: 'style', href: FontStyles500 },\n    { rel: 'preload', as: 'style', href: FontStyles600 },\n    { rel: 'preload', as: 'style', href: FontStyles700 },\n    { rel: 'preload', as: 'style', href: FontStyles800 },\n    //These should match the css preloads above to avoid css as render blocking resource\n    { rel: 'stylesheet', href: tailwindStylesheetUrl },\n    cssBundleHref ? { rel: 'stylesheet', href: cssBundleHref } : null,\n    process.env.NODE_ENV === 'development' ? { rel: 'stylesheet', href: rdtStylesheet } : null,\n    { rel: 'stylesheet', href: FontStyles100 },\n    { rel: 'stylesheet', href: FontStyles200 },\n    { rel: 'stylesheet', href: FontStyles300 },\n    { rel: 'stylesheet', href: FontStyles400 },\n    { rel: 'stylesheet', href: FontStyles500 },\n    { rel: 'stylesheet', href: FontStyles600 },\n    { rel: 'stylesheet', href: FontStyles700 },\n    { rel: 'stylesheet', href: FontStyles800 },\n    { rel: 'stylesheet', href: swiperStyles },\n    { rel: 'stylesheet', href: swiperPaginationStyles },\n    { rel: 'stylesheet', href: swiperNavigationStyles },\n    { rel: 'stylesheet', href: swiperThumbsStyles },\n    { rel: 'stylesheet', href: swiperAutoPlayStyles },\n    { rel: 'stylesheet', href: swiperFreeModeStyles },\n    { rel: 'stylesheet', href: nProgressStyles },\n    { rel: 'stylesheet', href: photoSwipeStyles },\n  ].filter(Boolean) as LinkDescriptor[];\n};\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const locale = await i18next.getLocale(request);\n  const gaTrackingId = process.env.GA_TRACKING_ID;\n  const user = await getUserFromCookie(request.headers.get('Cookie') || '');\n  const deviceDetect = getSelectorsByUserAgent(request.headers.get('User-Agent') || '');\n  const ipAddress = getClientIPAddress(request);\n  const locales = getClientLocales(request);\n  const nowDate = new Date();\n  const formatter = new Intl.DateTimeFormat(locale, {\n    year: 'numeric',\n    month: 'long',\n    day: 'numeric',\n  });\n  const toast = getToastSession(request);\n  const message = await toast.getMessage();\n\n  return json(\n    {\n      user: user || undefined,\n      locale,\n      genresMovie: await getListGenre('movie', locale),\n      genresTv: await getListGenre('tv', locale),\n      languages: await getListLanguages(),\n      gaTrackingId,\n      deviceDetect,\n      ENV: {\n        NODE_ENV: process.env.NODE_ENV,\n        VERCEL_GIT_COMMIT_SHA: process.env.VERCEL_GIT_COMMIT_SHA,\n        RESPONSIVE_IMAGES: process.env.RESPONSIVE_IMAGES,\n        IMAGE_PROXY: process.env.IMAGE_PROXY,\n      },\n      ipAddress,\n      locales,\n      nowDate: formatter.format(nowDate),\n      message,\n    },\n    {\n      headers: combineHeaders(\n        new Headers({\n          'Set-Cookie': await i18nCookie.serialize(locale),\n        }),\n        new Headers({\n          'Set-Cookie': await toast.commit(),\n        }),\n      ),\n    },\n  );\n};\n\nexport const meta: MetaFunction<typeof loader> = () => [\n  { title: 'Sora' },\n  { name: 'description', content: 'Watching movies, series, anime and more in Sora' },\n  {\n    name: 'keywords',\n    content:\n      'Sora, Sora Movies, Sora Series, Sora Anime, Sora Chill, SoraChill, watch movies, watch series, watch anime, watch movies online, watch series online, watch anime online, free movies, free series, free anime, free movies online, free series online, free anime online, watch movies free, watch series free, watch anime free, watch movies online free, watch series online free, watch anime online free',\n  },\n  { property: 'og:url', content: 'https://sorachill.vercel.app' },\n  { property: 'og:title', content: 'Sora' },\n  { property: 'og:image', content: 'https://sorachill.vercel.app/api/ogimage?it=home' },\n  { property: 'og:description', content: 'Watching movies, series, anime and more in Sora' },\n  { property: 'og:type', content: 'website' },\n  { property: 'og:site_name', content: 'Sora' },\n  { property: 'og:image:width', content: '1200' },\n  { property: 'og:image:height', content: '630' },\n  { name: 'twitter:card', content: 'summary_large_image' },\n  { name: 'twitter:site', content: '@sora' },\n  { name: 'twitter:image', content: 'https://sorachill.vercel.app/api/ogimage?it=home' },\n  { name: 'twitter:title', content: 'Sora' },\n  { name: 'twitter:description', content: 'Watching movies, series, anime and more in Sora' },\n];\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/\" key=\"home\">\n      <Home width={16} height={16} />\n    </BreadcrumbItem>\n  ),\n};\n\nfunction useLoadingIndicator() {\n  const navigation = useNavigation();\n\n  const fetchers = useFetchers();\n  /**\n   * This gets the state of every fetcher active on the app and combine it with\n   * the state of the global transition (Link and Form), then use them to\n   * determine if the app is idle or if it's loading.\n   * Here we consider both loading and submitting as loading.\n   */\n  const state = React.useMemo<'idle' | 'loading'>(() => {\n    const states = [navigation.state, ...fetchers.map((fetcher) => fetcher.state)];\n    if (states.every((item) => item === 'idle')) return 'idle';\n    return 'loading';\n  }, [navigation.state, fetchers]);\n\n  React.useEffect(() => {\n    // and when it's something else it means it's either submitting a form or\n    // waiting for the loaders of the next location so we start it\n    if (state === 'loading') NProgress.configure({ showSpinner: false }).start();\n    // when the state is idle then we can to complete the progress bar\n    if (state === 'idle') NProgress.configure({ showSpinner: false }).done();\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [navigation.state]);\n}\n\nfunction ElementScrollRestoration({\n  elementQuery,\n  ...props\n}: { elementQuery: string } & React.HTMLProps<HTMLScriptElement>) {\n  const STORAGE_KEY = `position:${elementQuery}`;\n  const navigation = useNavigation();\n  const location = useLocation();\n\n  const updatePositions = React.useCallback(() => {\n    const element = document.querySelector(elementQuery);\n    if (!element) return;\n    let positions = {};\n    try {\n      const rawPositions = JSON.parse(sessionStorage.getItem(STORAGE_KEY) || '{}');\n      if (typeof rawPositions === 'object' && rawPositions !== null) {\n        positions = rawPositions;\n      }\n    } catch (error) {\n      console.warn(`Error parsing scroll positions from sessionStorage:`, error);\n    }\n    const newPositions = {\n      ...positions,\n      [location.key]: element.scrollTop,\n    };\n    sessionStorage.setItem(STORAGE_KEY, JSON.stringify(newPositions));\n  }, [STORAGE_KEY, elementQuery, location.key]);\n\n  React.useEffect(() => {\n    if (navigation.state === 'idle') {\n      const element = document.querySelector(elementQuery);\n      if (!element) return;\n      try {\n        const positions = JSON.parse(sessionStorage.getItem(STORAGE_KEY) || '{}') as any;\n        const storedY = positions[window.history.state.key];\n        if (typeof storedY === 'number') {\n          element.scrollTop = storedY;\n        }\n      } catch (error: unknown) {\n        console.error(error);\n        sessionStorage.removeItem(STORAGE_KEY);\n      }\n    } else {\n      updatePositions();\n    }\n  }, [STORAGE_KEY, elementQuery, navigation.state, updatePositions]);\n\n  useBeforeUnload(() => {\n    updatePositions();\n  });\n\n  function restoreScroll(storageKey: string, elementQuery: string) {\n    const element = document.querySelector(elementQuery);\n    if (!element) {\n      console.warn(`Element not found: ${elementQuery}. Cannot restore scroll.`);\n      return;\n    }\n    if (!window.history.state || !window.history.state.key) {\n      const key = Math.random().toString(32).slice(2);\n      window.history.replaceState({ key }, '');\n    }\n    try {\n      const positions = JSON.parse(sessionStorage.getItem(storageKey) || '{}') as any;\n      const storedY = positions[window.history.state.key];\n      if (typeof storedY === 'number') {\n        element.scrollTop = storedY;\n      }\n    } catch (error: unknown) {\n      console.error(error);\n      sessionStorage.removeItem(storageKey);\n    }\n  }\n  return (\n    <script\n      {...props}\n      suppressHydrationWarning\n      dangerouslySetInnerHTML={{\n        __html: `(${restoreScroll})(${JSON.stringify(STORAGE_KEY)}, ${JSON.stringify(\n          elementQuery,\n        )})`,\n      }}\n    />\n  );\n}\n\nfunction useDetectSWUpdate() {\n  const [waitingWorker, setWaitingWorker] = React.useState<ServiceWorker | null>(null);\n  const [isUpdateAvailable, setIsUpdateAvailable] = React.useState(false);\n  React.useEffect(() => {\n    const detectSWUpdate = async () => {\n      if ('serviceWorker' in navigator) {\n        const registration = await navigator.serviceWorker.ready;\n        if (registration) {\n          registration.addEventListener('updatefound', () => {\n            const newWorker = registration.installing;\n            if (newWorker) {\n              console.log(\n                '🚀 ~ file: root.tsx:369 ~ registration.addEventListener ~ newWorker:',\n                newWorker,\n              );\n              newWorker.addEventListener('statechange', () => {\n                if (newWorker.state === 'installed') {\n                  logger.log('Service worker update found');\n                  setWaitingWorker(newWorker);\n                  setIsUpdateAvailable(true);\n                }\n              });\n            }\n          });\n          if (registration.waiting) {\n            setWaitingWorker(registration.waiting);\n            setIsUpdateAvailable(true);\n          }\n        }\n      }\n    };\n    detectSWUpdate();\n  }, []);\n  return {\n    waitingWorker,\n    isUpdateAvailable,\n  };\n}\n\nconst Document = ({ children, title }: DocumentProps) => {\n  const location = useLocation();\n  const { locale, gaTrackingId, ENV } = useLoaderData<typeof loader>();\n  const { i18n } = useTranslation();\n  const isBot = useIsBot();\n  const isHydrated = useHydrated();\n  useLoadingIndicator();\n  const color = React.useMemo(() => {\n    if (isHydrated) {\n      return getComputedStyle(document.documentElement).getPropertyValue(\n        '--theme-background-title-bar',\n      );\n    }\n    return '0 0 0';\n  }, [isHydrated]);\n\n  React.useEffect(() => {\n    if (gaTrackingId?.length) {\n      gtag.pageview(location.pathname, gaTrackingId);\n    }\n  }, [location, gaTrackingId]);\n\n  useSWEffect();\n\n  return (\n    <html lang={locale} dir={i18n.dir()} suppressHydrationWarning>\n      <head>\n        {title ? <title>{title}</title> : null}\n        <meta charSet=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n        <meta name=\"darkreader-lock\" content=\"disable darkreader\" />\n        <meta name=\"msvalidate.01\" content=\"1445DD7580898781011249BF246A21AD\" />\n        <meta name=\"theme-color\" content={`hsl(${color})`} />\n        <Meta />\n        <Links />\n      </head>\n      <body>\n        {process.env.NODE_ENV === 'development' || !gaTrackingId || isBot ? null : (\n          <>\n            <script async src={`https://www.googletagmanager.com/gtag/js?id=${gaTrackingId}`} />\n            <script\n              async\n              id=\"gtag-init\"\n              dangerouslySetInnerHTML={{\n                __html: `\n                  window.dataLayer = window.dataLayer || [];\n                  function gtag(){dataLayer.push(arguments);}\n                  gtag('js', new Date());\n\n                  gtag('config', '${gaTrackingId}', {\n                    page_path: window.location.pathname,\n                  });\n                `,\n              }}\n            />\n          </>\n        )}\n        <script\n          dangerouslySetInnerHTML={{\n            __html: `window.process = ${JSON.stringify({\n              env: ENV,\n            })}`,\n          }}\n        />\n        {children}\n        <ElementScrollRestoration elementQuery=\"[data-restore-scroll='true']\" />\n        {isBot ? null : <Scripts />}\n        {process.env.NODE_ENV === 'development' ? <LiveReload /> : null}\n      </body>\n    </html>\n  );\n};\n\nconst App = () => {\n  const isHydrated = useHydrated();\n  const { user, locale, message } = useLoaderData<typeof loader>();\n  const isBot = useIsBot();\n  useChangeLanguage(locale);\n  useToast(message);\n  const { waitingWorker, isUpdateAvailable } = useDetectSWUpdate();\n\n  React.useEffect(() => {\n    const reloadPage = () => {\n      logger.log('Service worker updated');\n      waitingWorker?.postMessage('skipWaiting');\n      window.location.reload();\n    };\n    if (isUpdateAvailable) {\n      toast.success('Update Available', {\n        description: 'A new version of Sora is available.',\n        action: {\n          label: 'Update',\n          onClick: () => reloadPage(),\n        },\n        duration: Infinity,\n      });\n    }\n  }, [isUpdateAvailable, waitingWorker]);\n\n  return (\n    <Document>\n      <WrapBalancerProvider>\n        <RemixThemesProvider\n          defaultTheme=\"system\"\n          attribute=\"class\"\n          enableColorScheme\n          enableSystem\n          themes={listThemes}\n          value={themeValues}\n        >\n          <AnimatePresence>\n            {!isHydrated && process.env.NODE_ENV !== 'development' && !isBot ? (\n              <motion.div\n                initial={{ opacity: 1 }}\n                animate={{ opacity: 1 }}\n                exit={{ opacity: 0 }}\n                transition={{ duration: 0.5 }}\n                className=\"fixed left-0 top-0 z-[9999] block h-full w-full bg-background\"\n              >\n                <div className=\"relative top-1/2 m-auto mt-[-77px] block h-0 w-0\">\n                  <div className=\"mb-5 flex\titems-center justify-center\">\n                    <Image\n                      width=\"100px\"\n                      height=\"100px\"\n                      className=\"mr-5 rounded-full\"\n                      title=\"Logo Loading\"\n                      alt=\"Logo Loading\"\n                      src={logoLoading}\n                      placeholder=\"empty\"\n                      responsive={[\n                        {\n                          size: {\n                            width: 100,\n                            height: 100,\n                          },\n                        },\n                      ]}\n                      dprVariants={[1, 3]}\n                      options={{\n                        contentType: MimeType.WEBP,\n                      }}\n                    />\n                    <h1 className=\"bg-gradient-to-tr from-secondary to-primary to-50% bg-clip-text !text-3xl font-bold tracking-normal text-transparent md:!text-5xl\">\n                      SORA\n                    </h1>\n                  </div>\n                  <div className=\"h-9 w-9 animate-spin\">\n                    <div className=\"h-full w-full rounded-[50%] border-4 border-y-primary\" />\n                  </div>\n                </div>\n              </motion.div>\n            ) : null}\n          </AnimatePresence>\n          <NextUIv2Provider>\n            <Layout user={user as User | undefined} />\n          </NextUIv2Provider>\n        </RemixThemesProvider>\n      </WrapBalancerProvider>\n    </Document>\n  );\n};\n\nexport function ErrorBoundary() {\n  const error = useRouteError();\n  const isProd = process.env.NODE_ENV === 'production';\n\n  if (isRouteErrorResponse(error)) {\n    let message;\n    switch (error.status) {\n      case 401:\n        message = isProd\n          ? 'Oops! Looks like you tried to visit a page that you do not have access to.'\n          : error.data;\n        break;\n      case 404:\n        message = isProd\n          ? 'Oops! Looks like you tried to visit a page that does not exist.'\n          : error.data;\n        break;\n      default:\n        throw new Error(error.data || error.statusText);\n    }\n    return (\n      <Document title={`${error.status} ${error.statusText}`}>\n        <RemixThemesProvider\n          defaultTheme=\"system\"\n          attribute=\"class\"\n          enableColorScheme\n          enableSystem\n          themes={listThemes}\n          value={themeValues}\n        >\n          <div className=\"flex h-screen flex-col items-center justify-center gap-y-4\">\n            <NextUIImage width={480} src={pageNotFound} alt=\"404\" className=\"object-cover\" />\n            <h1 className=\"text-center text-warning\">\n              {error.status} {error.statusText}\n            </h1>\n            <p>{message}</p>\n            <div className=\"flex w-full flex-row items-center justify-center gap-x-4\">\n              <Button\n                size=\"md\"\n                variant=\"ghost\"\n                color=\"success\"\n                startContent={<Home />}\n                type=\"button\"\n                onPress={() => {\n                  window.location.href = '/';\n                }}\n              >\n                Back to Home\n              </Button>\n              <Button\n                size=\"md\"\n                variant=\"ghost\"\n                color=\"warning\"\n                startContent={<Refresh filled />}\n                type=\"button\"\n                onPress={() => {\n                  window.location.reload();\n                }}\n              >\n                Reload Page\n              </Button>\n            </div>\n          </div>\n        </RemixThemesProvider>\n      </Document>\n    );\n  } else if (error instanceof Error) {\n    console.log(error);\n    return (\n      <Document title=\"Error!\">\n        <RemixThemesProvider\n          defaultTheme=\"system\"\n          attribute=\"class\"\n          enableColorScheme\n          enableSystem\n          themes={listThemes}\n          value={themeValues}\n        >\n          <div className=\"flex h-screen flex-col items-center justify-center gap-y-4\">\n            <NextUIImage width={480} src={pageNotFound} alt=\"404\" className=\"object-cover\" />\n            <h1 className=\"text-center text-danger\">There was an error</h1>\n            <p>{error.message}</p>\n            <div className=\"flex w-full flex-row items-center justify-center gap-x-4\">\n              <Button\n                size=\"md\"\n                variant=\"ghost\"\n                color=\"success\"\n                startContent={<Home />}\n                type=\"button\"\n                onPress={() => {\n                  window.location.href = '/';\n                }}\n              >\n                Back to Home\n              </Button>\n              <Button\n                size=\"md\"\n                variant=\"ghost\"\n                color=\"warning\"\n                startContent={<Refresh filled />}\n                type=\"button\"\n                onPress={() => {\n                  window.location.reload();\n                }}\n              >\n                Reload Page\n              </Button>\n            </div>\n          </div>\n        </RemixThemesProvider>\n      </Document>\n    );\n  } else {\n    return (\n      <Document title=\"Error!\">\n        <RemixThemesProvider\n          defaultTheme=\"system\"\n          attribute=\"class\"\n          enableColorScheme\n          enableSystem\n          themes={listThemes}\n          value={themeValues}\n        >\n          <div className=\"flex h-screen flex-col items-center justify-center gap-y-4\">\n            <NextUIImage width={480} src={pageNotFound} alt=\"404\" className=\"object-cover\" />\n            <h1 className=\"text-center text-danger\">Unknown error</h1>\n            <div className=\"flex w-full flex-row items-center justify-center gap-x-4\">\n              <Button\n                size=\"md\"\n                variant=\"ghost\"\n                color=\"success\"\n                startContent={<Home />}\n                type=\"button\"\n                onPress={() => {\n                  window.location.href = '/';\n                }}\n              >\n                Back to Home\n              </Button>\n              <Button\n                size=\"md\"\n                variant=\"ghost\"\n                color=\"warning\"\n                startContent={<Refresh filled />}\n                type=\"button\"\n                onPress={() => {\n                  window.location.reload();\n                }}\n              >\n                Reload Page\n              </Button>\n            </div>\n          </div>\n        </RemixThemesProvider>\n      </Document>\n    );\n  }\n}\n\nlet AppExport = App;\n\nif (process.env.NODE_ENV === 'development') {\n  const { withDevTools } = require('remix-development-tools');\n  AppExport = withDevTools(AppExport);\n}\n\nexport default AppExport;\n"
  },
  {
    "path": "app/routes/[manifest.webmanifest].ts",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\n\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n  return json(\n    {\n      short_name: 'Sora',\n      name: 'Sora',\n      theme_color: '#0072F5',\n      background_color: '#000000',\n      display: 'standalone',\n      display_override: ['standalone', 'browser', 'fullscreen'],\n      description: 'An app for watching movies, anime, reading manga and more',\n      start_url: '/',\n      dir: 'ltr',\n      scope: '/',\n      lang: locale,\n      orientation: 'natural',\n      categories: ['books', 'entertainment', 'music', 'news', 'personalization', 'photo'],\n      shortcuts: [\n        {\n          name: 'Movies',\n          url: '/movies',\n          description: 'Explore latest movies',\n          icons: [\n            {\n              src: '/favicons/windows11/Square44x44Logo.targetsize-96.png',\n              sizes: '96x96',\n            },\n          ],\n        },\n        {\n          name: 'Tv Shows',\n          url: '/tv-shows',\n          description: 'Explore latest tv shows',\n          icons: [\n            {\n              src: '/favicons/windows11/Square44x44Logo.targetsize-96.png',\n              sizes: '96x96',\n            },\n          ],\n        },\n        {\n          name: 'Anime',\n          url: '/anime',\n          description: 'Explore latest anime',\n          icons: [\n            {\n              src: '/favicons/windows11/Square44x44Logo.targetsize-96.png',\n              sizes: '96x96',\n            },\n          ],\n        },\n      ],\n      screenshots: [\n        {\n          src: '/images/screenshot.png',\n          type: 'image/png',\n          sizes: '1901x959',\n          platform: 'wide',\n          label: 'Homescreen of Sora in darkmode',\n        },\n        {\n          src: '/images/screenshot_2.png',\n          type: 'image/png',\n          sizes: '1280x800',\n          platform: 'wide',\n          label: 'Homescreen of Sora in lightmode',\n        },\n        {\n          src: '/images/screenshot_3.png',\n          type: 'image/png',\n          sizes: '482x995',\n          platform: 'narrow',\n          label: 'Homescreen of Sora in mobile',\n        },\n      ],\n      icons: [\n        {\n          src: '/favicons/windows11/SmallTile.scale-100.png',\n          sizes: '71x71',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/SmallTile.scale-125.png',\n          sizes: '89x89',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/SmallTile.scale-150.png',\n          sizes: '107x107',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/SmallTile.scale-200.png',\n          sizes: '142x142',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/SmallTile.scale-400.png',\n          sizes: '284x284',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square150x150Logo.scale-100.png',\n          sizes: '150x150',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square150x150Logo.scale-125.png',\n          sizes: '188x188',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square150x150Logo.scale-150.png',\n          sizes: '225x225',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square150x150Logo.scale-200.png',\n          sizes: '300x300',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square150x150Logo.scale-400.png',\n          sizes: '600x600',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Wide310x150Logo.scale-100.png',\n          sizes: '310x150',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Wide310x150Logo.scale-125.png',\n          sizes: '388x188',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Wide310x150Logo.scale-150.png',\n          sizes: '465x225',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Wide310x150Logo.scale-200.png',\n          sizes: '620x300',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Wide310x150Logo.scale-400.png',\n          sizes: '1240x600',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/LargeTile.scale-100.png',\n          sizes: '310x310',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/LargeTile.scale-125.png',\n          sizes: '388x388',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/LargeTile.scale-150.png',\n          sizes: '465x465',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/LargeTile.scale-200.png',\n          sizes: '620x620',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/LargeTile.scale-400.png',\n          sizes: '1240x1240',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.scale-100.png',\n          sizes: '44x44',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.scale-125.png',\n          sizes: '55x55',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.scale-150.png',\n          sizes: '66x66',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.scale-200.png',\n          sizes: '88x88',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.scale-400.png',\n          sizes: '176x176',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/StoreLogo.scale-100.png',\n          sizes: '50x50',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/StoreLogo.scale-125.png',\n          sizes: '63x63',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/StoreLogo.scale-150.png',\n          sizes: '75x75',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/StoreLogo.scale-200.png',\n          sizes: '100x100',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/StoreLogo.scale-400.png',\n          sizes: '200x200',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/SplashScreen.scale-100.png',\n          sizes: '620x300',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/SplashScreen.scale-125.png',\n          sizes: '775x375',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/SplashScreen.scale-150.png',\n          sizes: '930x450',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/SplashScreen.scale-200.png',\n          sizes: '1240x600',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/SplashScreen.scale-400.png',\n          sizes: '2480x1200',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.targetsize-16.png',\n          sizes: '16x16',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.targetsize-20.png',\n          sizes: '20x20',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.targetsize-24.png',\n          sizes: '24x24',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.targetsize-30.png',\n          sizes: '30x30',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.targetsize-32.png',\n          sizes: '32x32',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.targetsize-36.png',\n          sizes: '36x36',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.targetsize-40.png',\n          sizes: '40x40',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.targetsize-44.png',\n          sizes: '44x44',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.targetsize-48.png',\n          sizes: '48x48',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.targetsize-60.png',\n          sizes: '60x60',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.targetsize-64.png',\n          sizes: '64x64',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.targetsize-72.png',\n          sizes: '72x72',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.targetsize-80.png',\n          sizes: '80x80',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.targetsize-96.png',\n          sizes: '96x96',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.targetsize-256.png',\n          sizes: '256x256',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-unplated_targetsize-16.png',\n          sizes: '16x16',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-unplated_targetsize-20.png',\n          sizes: '20x20',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-unplated_targetsize-24.png',\n          sizes: '24x24',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-unplated_targetsize-30.png',\n          sizes: '30x30',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-unplated_targetsize-32.png',\n          sizes: '32x32',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-unplated_targetsize-36.png',\n          sizes: '36x36',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-unplated_targetsize-40.png',\n          sizes: '40x40',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-unplated_targetsize-44.png',\n          sizes: '44x44',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-unplated_targetsize-48.png',\n          sizes: '48x48',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-unplated_targetsize-60.png',\n          sizes: '60x60',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-unplated_targetsize-64.png',\n          sizes: '64x64',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-unplated_targetsize-72.png',\n          sizes: '72x72',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-unplated_targetsize-80.png',\n          sizes: '80x80',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-unplated_targetsize-96.png',\n          sizes: '96x96',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-unplated_targetsize-256.png',\n          sizes: '256x256',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-lightunplated_targetsize-16.png',\n          sizes: '16x16',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-lightunplated_targetsize-20.png',\n          sizes: '20x20',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-lightunplated_targetsize-24.png',\n          sizes: '24x24',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-lightunplated_targetsize-30.png',\n          sizes: '30x30',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-lightunplated_targetsize-32.png',\n          sizes: '32x32',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-lightunplated_targetsize-36.png',\n          sizes: '36x36',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-lightunplated_targetsize-40.png',\n          sizes: '40x40',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-lightunplated_targetsize-44.png',\n          sizes: '44x44',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-lightunplated_targetsize-48.png',\n          sizes: '48x48',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-lightunplated_targetsize-60.png',\n          sizes: '60x60',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-lightunplated_targetsize-64.png',\n          sizes: '64x64',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-lightunplated_targetsize-72.png',\n          sizes: '72x72',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-lightunplated_targetsize-80.png',\n          sizes: '80x80',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-lightunplated_targetsize-96.png',\n          sizes: '96x96',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/windows11/Square44x44Logo.altform-lightunplated_targetsize-256.png',\n          sizes: '256x256',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/android/android-launchericon-512-512.png',\n          sizes: '512x512',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/android/android-launchericon-192-192.png',\n          sizes: '192x192',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/android/android-launchericon-144-144.png',\n          sizes: '144x144',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/android/android-launchericon-96-96.png',\n          sizes: '96x96',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/android/android-launchericon-72-72.png',\n          sizes: '72x72',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/android/android-launchericon-48-48.png',\n          sizes: '48x48',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/16.png',\n          sizes: '16x16',\n          type: 'image/png',\n          purpose: 'any',\n        },\n        {\n          src: '/favicons/ios/20.png',\n          sizes: '20x20',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/29.png',\n          sizes: '29x29',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/32.png',\n          sizes: '32x32',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/40.png',\n          sizes: '40x40',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/50.png',\n          sizes: '50x50',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/57.png',\n          sizes: '57x57',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/58.png',\n          sizes: '58x58',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/60.png',\n          sizes: '60x60',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/64.png',\n          sizes: '64x64',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/72.png',\n          sizes: '72x72',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/76.png',\n          sizes: '76x76',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/80.png',\n          sizes: '80x80',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/87.png',\n          sizes: '87x87',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/100.png',\n          sizes: '100x100',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/114.png',\n          sizes: '114x114',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/120.png',\n          sizes: '120x120',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/128.png',\n          sizes: '128x128',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/144.png',\n          sizes: '144x144',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/152.png',\n          sizes: '152x152',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/167.png',\n          sizes: '167x167',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/180.png',\n          sizes: '180x180',\n          type: 'image/png',\n          purpose: 'any',\n        },\n        {\n          src: '/favicons/ios/192.png',\n          sizes: '192x192',\n          type: 'image/png',\n          purpose: 'maskable',\n        },\n        {\n          src: '/favicons/ios/256.png',\n          sizes: '256x256',\n          type: 'image/png',\n        },\n        {\n          src: '/favicons/ios/512.png',\n          sizes: '512x512',\n          type: 'image/png',\n          purpose: 'maskable',\n        },\n        {\n          src: '/favicons/ios/1024.png',\n          sizes: '1024x1024',\n          type: 'image/png',\n        },\n      ],\n    },\n    {\n      headers: {\n        'Cache-Control': 'public, max-age=600',\n        'Content-Type': 'application/manifest+json',\n      },\n    },\n  );\n};\n"
  },
  {
    "path": "app/routes/_auth+/sign-in.tsx",
    "content": "import { json, type ActionFunctionArgs, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useActionData, useLocation } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport {\n  commitAuthCookie,\n  getSessionFromCookie,\n  requestPayload,\n  signInWithPassword,\n} from '~/services/supabase';\nimport { addToast, redirectWithToast } from '~/utils/server/toast-session.server';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport AuthForm from '~/components/elements/shared/AuthForm';\n\ntype ActionData = {\n  error: string | null;\n};\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Sign In' },\n  { name: 'description', content: 'Sign in to your Sora account.' },\n  { property: 'og:title', content: 'Sora - Sign In' },\n  { property: 'og:description', content: 'Sign in to your Sora account.' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/sign-in' },\n  { property: 'twitter:title', content: 'Sora - Sign In' },\n  { property: 'twitter:description', content: 'Sign in to your Sora account.' },\n]);\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n  const { searchParams } = new URL(request.url);\n  const data = await request.clone().formData();\n\n  const email = data.get('email')?.toString();\n  const password = data.get('password')?.toString();\n\n  if (!email || !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}$/i.test(email)) {\n    const cookie = await addToast(request, {\n      type: 'error',\n      title: 'Invalid Email!',\n      description: 'Please enter a valid email!',\n    });\n    return json<ActionData>(\n      { error: 'Please enter a valid email!' },\n      { headers: { 'Set-Cookie': cookie } },\n    );\n  }\n\n  if (!password) {\n    const cookie = await addToast(request, {\n      type: 'error',\n      title: 'Password is required!',\n      description: 'Please enter your password!',\n    });\n    return json<ActionData>(\n      { error: 'Password is required!' },\n      { headers: { 'Set-Cookie': cookie } },\n    );\n  }\n\n  const {\n    data: { session },\n    error,\n  } = await signInWithPassword(email, password);\n\n  if (error) {\n    const cookie = await addToast(request, {\n      type: 'error',\n      title: 'Invalid Credentials!',\n      description: 'Please enter valid credentials!',\n    });\n    return json<ActionData>({ error: error.message }, { headers: { 'Set-Cookie': cookie } });\n  }\n\n  if (!session) {\n    const cookie = await addToast(request, {\n      type: 'error',\n      title: 'Something went wrong!',\n      description: 'Please sign in again!',\n    });\n    return json<ActionData>(\n      { error: 'Something went wrong. Please sign in again!' },\n      { headers: { 'Set-Cookie': cookie } },\n    );\n  }\n\n  const authCookie = await getSessionFromCookie(request.headers.get('Cookie'));\n\n  if (authCookie.has('auth_token')) {\n    return redirectWithToast(request, searchParams.get('ref') || '/', {\n      type: 'default',\n      title: 'Already Signed In!',\n      description: 'You are already signed in!',\n    });\n  }\n  const payload = process.env.NODE_ENV === 'production' ? await requestPayload(request) : undefined;\n\n  authCookie.set('auth_token', {\n    access_token: session.access_token,\n    refresh_token: session.refresh_token,\n    expires_at: Date.now() + (session.expires_in - 10) * 1000,\n    req_payload: payload,\n  });\n\n  const ref = (searchParams.get('ref') || '/').replace('_0x3F_', '?').replace('_0x26', '&');\n\n  return redirectWithToast(\n    request,\n    ref,\n    {\n      type: 'success',\n      title: 'Signed In!',\n      description: 'You have successfully signed in!',\n    },\n    { headers: { 'Set-Cookie': await commitAuthCookie(authCookie) } },\n  );\n};\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const session = await getSessionFromCookie(request.headers.get('Cookie'));\n  const { searchParams } = new URL(request.url);\n  const ref = (searchParams.get('ref') || '/').replace('_0x3F_', '?').replace('_0x26', '&');\n\n  if (session.has('auth_token')) {\n    return redirectWithToast(request, ref, {\n      type: 'default',\n      title: 'Already Signed In!',\n      description: 'You are already signed in!',\n    });\n  }\n\n  return null;\n};\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/sign-in\" key=\"sign-in\">\n      Sign In\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Sign In',\n    showImage: false,\n  }),\n};\n\nconst SignInPage = () => {\n  const actionData = useActionData<ActionData>();\n  const location = useLocation();\n  const code = new URLSearchParams(location.search).get('code');\n  return <AuthForm type=\"sign-in\" error={actionData?.error} code={code} />;\n};\n\nexport default SignInPage;\n"
  },
  {
    "path": "app/routes/_auth+/sign-out.tsx",
    "content": "import { type ActionFunctionArgs } from '@remix-run/node';\n\nimport { destroyAuthCookie, getSessionFromCookie } from '~/services/supabase';\nimport { redirectWithToast } from '~/utils/server/toast-session.server';\n\nexport const loader = async ({ request }: ActionFunctionArgs) => {\n  const { searchParams } = new URL(request.url);\n  const session = await getSessionFromCookie(request.headers.get('Cookie'));\n  const ref = (searchParams.get('ref') || '/').replace('_0x3F_', '?').replace('_0x26', '&');\n\n  return redirectWithToast(\n    request,\n    ref,\n    {\n      type: 'success',\n      title: 'Logged out',\n      description: 'You have been logged out.',\n    },\n    { headers: { 'Set-Cookie': await destroyAuthCookie(session) } },\n  );\n};\n"
  },
  {
    "path": "app/routes/_auth+/sign-up.tsx",
    "content": "import { json, type ActionFunctionArgs, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useActionData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport sgConfigs from '~/services/configs.server';\nimport {\n  commitAuthCookie,\n  getSessionFromCookie,\n  requestPayload,\n  signUp,\n} from '~/services/supabase';\nimport encode from '~/utils/encode';\nimport { addToast, redirectWithToast } from '~/utils/server/toast-session.server';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport AuthForm from '~/components/elements/shared/AuthForm';\n\ntype ActionData = {\n  errorCode?: string | null;\n  error?: string;\n};\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Sign Up' },\n  { name: 'description', content: 'Sign up for a Sora account.' },\n  { property: 'og:title', content: 'Sora - Sign Up' },\n  { property: 'og:description', content: 'Sign up for a Sora account.' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/sign-up' },\n  { property: 'twitter:title', content: 'Sora - Sign Up' },\n  { property: 'twitter:description', content: 'Sign up for a Sora account.' },\n]);\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n  const { searchParams } = new URL(request.url);\n  const data = await request.clone().formData();\n\n  const email = data.get('email')?.toString();\n  const password = data.get('password')?.toString();\n  const rePassword = data.get('re-password')?.toString();\n  const inviteCode = data.get('invite-code')?.toString();\n\n  if (sgConfigs.__invitedSignUpOnly) {\n    if (!inviteCode) {\n      return json<ActionData>({ errorCode: 'noInviteCode' });\n    }\n    if (encode(inviteCode || '') !== sgConfigs.__invitedSignUpKey) {\n      return json<ActionData>({ errorCode: 'invalidInviteCode' });\n    }\n  }\n\n  if (!email || !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}$/i.test(email)) {\n    const cookie = await addToast(request, {\n      type: 'error',\n      title: 'Invalid Email',\n      description: 'Please enter a valid email address.',\n    });\n    return json<ActionData>({ errorCode: 'invalidEmail' }, { headers: { 'Set-Cookie': cookie } });\n  }\n\n  if (!password || !rePassword || password !== rePassword) {\n    const cookie = await addToast(request, {\n      type: 'error',\n      title: 'Unmatched Password',\n      description: 'Please enter the same password.',\n    });\n    return json<ActionData>(\n      { errorCode: 'unmatchedPassword' },\n      { headers: { 'Set-Cookie': cookie } },\n    );\n  }\n\n  const {\n    data: { session },\n    error,\n  } = await signUp(email, password);\n\n  if (error) {\n    const cookie = await addToast(request, {\n      type: 'error',\n      title: 'Sign Up Failed',\n      description: error.message,\n    });\n    return json<ActionData>({ error: error.message }, { headers: { 'Set-Cookie': cookie } });\n  }\n\n  if (!session) {\n    return redirectWithToast(\n      request,\n      `/sign-in?ref=${searchParams.get('ref') || '/'}&code=201-email`,\n      {\n        type: 'success',\n        title: 'Sign Up Successfully',\n        description: 'Please confirm your email before log in',\n      },\n    );\n  }\n\n  const authCookie = await getSessionFromCookie(request.headers.get('Cookie'));\n\n  if (authCookie.has('auth_token')) {\n    return redirectWithToast(request, searchParams.get('ref') || '/', {\n      type: 'error',\n      title: 'Sign Up Failed',\n      description: 'You are already signed in.',\n    });\n  }\n\n  const payload = process.env.NODE_ENV === 'production' ? await requestPayload(request) : undefined;\n  authCookie.set('auth_token', {\n    access_token: session.access_token,\n    refresh_token: session.refresh_token,\n    expires_at: Date.now() + (session.expires_in - 10) * 1000,\n    req_payload: payload,\n  });\n\n  const ref = (searchParams.get('ref') || '/').replace('_0x3F_', '?').replace('_0x26', '&');\n\n  return redirectWithToast(\n    request,\n    ref,\n    {\n      type: 'success',\n      title: 'Sign Up Successfully',\n      description: 'Welcome to Sora!',\n    },\n    { headers: { 'Set-Cookie': await commitAuthCookie(authCookie) } },\n  );\n};\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const { searchParams } = new URL(request.url);\n  const session = await getSessionFromCookie(request.headers.get('Cookie'));\n  const ref = (searchParams.get('ref') || '/').replace('_0x3F_', '?').replace('_0x26', '&');\n\n  if (session.has('auth_token')) {\n    return redirectWithToast(request, ref, {\n      type: 'error',\n      title: 'Sign Up Failed',\n      description: 'You are already signed in.',\n    });\n  }\n\n  return null;\n};\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/sign-up\" key=\"sign-up\">\n      Sign Up\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Sign Up',\n    showImage: false,\n  }),\n};\n\nconst SignUpPage = () => {\n  const actionData = useActionData<ActionData>();\n\n  return <AuthForm type=\"sign-up\" error={actionData?.error} errorCode={actionData?.errorCode} />;\n};\n\nexport default SignUpPage;\n"
  },
  {
    "path": "app/routes/_index.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { motion } from 'framer-motion';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport type { IMedia } from '~/types/media';\nimport { getAnimePopular } from '~/services/consumet/anilist/anilist.server';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport {\n  getListDiscover,\n  getListMovies,\n  getListPeople,\n  getTrending,\n} from '~/services/tmdb/tmdb.server';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport featuredList from '~/constants/featuredList';\nimport MediaList from '~/components/media/MediaList';\n\nexport const handle: Handle = {\n  disableLayoutPadding: true,\n  miniTitle: () => ({\n    title: 'Home',\n    showImage: false,\n  }),\n};\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page'));\n  if (!page && (page < 1 || page > 1000)) page = 1;\n\n  const [todayTrending, movies, shows, anime, people] = await Promise.all([\n    getTrending('all', 'day', locale, page),\n    getListMovies('popular', locale, page),\n    getListDiscover(\n      'tv',\n      undefined,\n      undefined,\n      locale,\n      page,\n      undefined,\n      undefined,\n      undefined,\n      undefined,\n      undefined,\n      100,\n    ),\n    getAnimePopular(page, 16),\n    getListPeople('popular', locale, page),\n  ]);\n\n  return json(\n    {\n      todayTrending: todayTrending && todayTrending.items && todayTrending.items.slice(0, 10),\n      movies: movies && movies.items && movies.items.slice(0, 16),\n      shows: shows && shows.items && shows.items.slice(0, 16),\n      popularAnime: anime && (anime.results as IMedia[]),\n      people: people && people.items && people.items.slice(0, 16),\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.home,\n      },\n    },\n  );\n};\n\nconst RootIndex = () => {\n  const { movies, shows, popularAnime, people, todayTrending } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const location = useLocation();\n  const navigate = useNavigate();\n  const { t } = useTranslation();\n\n  const onClickViewMore = (type: 'movies' | 'tv-shows' | 'people') => {\n    if (type === 'people') navigate(`/${type}`);\n    else navigate(`/${type}/popular`);\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ x: '-10%', opacity: 0 }}\n      animate={{ x: '0', opacity: 1 }}\n      exit={{ y: '-10%', opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"mt-[72px] flex w-full flex-col items-center justify-center sm:mt-0\"\n    >\n      <MediaList\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={todayTrending}\n        key=\"slider-banner-home\"\n        listType=\"slider-banner\"\n      />\n      <div className=\"mt-9 flex w-full flex-col items-center justify-start px-3 sm:px-5\">\n        <MediaList\n          genresMovie={rootData?.genresMovie}\n          genresTv={rootData?.genresTv}\n          items={movies}\n          itemsType=\"movie\"\n          key=\"slider-card-popular-movies\"\n          listName={t('popular-movies')}\n          listType=\"slider-card\"\n          navigationButtons\n          onClickViewMore={() => onClickViewMore('movies')}\n          showMoreList\n        />\n        <MediaList\n          genresMovie={rootData?.genresMovie}\n          genresTv={rootData?.genresTv}\n          items={shows}\n          itemsType=\"tv\"\n          key=\"slider-card-popular-tv\"\n          listName={t('popular-tv-shows')}\n          listType=\"slider-card\"\n          navigationButtons\n          onClickViewMore={() => onClickViewMore('tv-shows')}\n          showMoreList\n        />\n        <MediaList\n          items={popularAnime}\n          itemsType=\"anime\"\n          key=\"slider-card-popular-anime\"\n          listName={t('popular-anime')}\n          listType=\"slider-card\"\n          navigationButtons\n          onClickViewMore={() => navigate('/anime/popular')}\n          showMoreList\n        />\n        <MediaList\n          coverItem={featuredList}\n          isCoverCard\n          key=\"slider-card-featured-lists\"\n          listName={t('featured-lists')}\n          listType=\"slider-card\"\n          navigationButtons\n          onClickViewMore={() => navigate('/lists')}\n          showMoreList\n        />\n        <MediaList\n          items={people}\n          itemsType=\"people\"\n          key=\"slider-card-popular-people\"\n          listName={t('popular-people')}\n          listType=\"slider-card\"\n          navigationButtons\n          onClickViewMore={() => onClickViewMore('people')}\n          showMoreList\n        />\n      </div>\n    </motion.div>\n  );\n};\n\nexport default RootIndex;\n"
  },
  {
    "path": "app/routes/admin+/cache.tsx",
    "content": "import { useEffect, useState } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { Spacer } from '@nextui-org/spacer';\nimport type { PressEvent } from '@react-aria/interactions';\nimport { json, type ActionFunctionArgs, type LoaderFunctionArgs } from '@remix-run/node';\nimport {\n  useFetcher,\n  useLoaderData,\n  useLocation,\n  useNavigate,\n  useSearchParams,\n} from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport type { CacheEntry } from 'cachified';\nimport { motion } from 'framer-motion';\n\nimport type { Handle } from '~/types/handle';\nimport { getUserFromCookie } from '~/services/supabase';\nimport { getAllCacheKeys, lruCache, searchCacheKeys } from '~/utils/server/cache.server';\nimport { redirectWithToast } from '~/utils/server/toast-session.server';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport {\n  Dialog,\n  DialogContent,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from '~/components/elements/Dialog';\nimport SearchForm from '~/components/elements/SearchForm';\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const cookie = request.headers.get('Cookie');\n  if (!cookie) {\n    return redirectWithToast(request, '/sign-in', {\n      title: 'Access Denied',\n      type: 'error',\n      description: 'You need to login to access this page',\n    });\n  }\n  const user = await getUserFromCookie(cookie as string);\n  if (!user) {\n    return redirectWithToast(request, '/sign-in', {\n      title: 'Access Denied',\n      type: 'error',\n      description: 'You need to login to access this page',\n    });\n  }\n  const admin = process.env.ADMIN_ID?.split(',');\n  if (user && !admin?.includes(user.id)) {\n    return redirectWithToast(request, '/', {\n      title: 'Access Denied',\n      type: 'error',\n      description: \"You don't have permission to access this page.\",\n    });\n  }\n  const url = new URL(request.url);\n  const query = url.searchParams.get('query');\n  let cacheKeys: Array<string>;\n  if (typeof query === 'string') {\n    cacheKeys = await searchCacheKeys(query);\n  } else {\n    cacheKeys = await getAllCacheKeys();\n  }\n  return json({ cacheKeys });\n};\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n  const formData = await request.formData();\n  const cacheKey = formData.get('cacheKey') as string;\n  if (typeof cacheKey !== 'string') {\n    return json({ message: 'Invalid cache key' }, { status: 400 });\n  }\n  lruCache.delete(cacheKey);\n  return json({ success: true });\n};\n\nexport const meta = mergeMeta(() => [\n  { title: 'Cache Management' },\n  { name: 'description', content: 'Cache Management' },\n]);\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/admin/cache\" key=\"cache-admin\">\n      Cache Management\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Cache Management',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst CacheKeyRow = ({ cacheKey }: { cacheKey: string }) => {\n  const fetcher = useFetcher();\n  const fetchCacheData = useFetcher<{\n    cacheKey: string;\n    value: CacheEntry<unknown>;\n  }>();\n  const [doubleCheck, setDoubleCheck] = useState(false);\n  const [IsShowDetailCache, setIsShowDetailCache] = useState(false);\n  const [cacheValue, setCacheValue] = useState<CacheEntry<unknown> | null>(null);\n  const closeHandler = () => {\n    setIsShowDetailCache(false);\n  };\n  const handleOpenDetailCache = (cacheKey: string, e: PressEvent) => {\n    if (e.pointerType === 'keyboard') {\n      setIsShowDetailCache(true);\n    }\n    fetchCacheData.load(`/admin/cache/${cacheKey}`);\n  };\n  useEffect(() => {\n    if (fetchCacheData.data && fetchCacheData.data.cacheKey) {\n      setCacheValue(fetchCacheData.data.value as CacheEntry<unknown>);\n    }\n  }, [fetchCacheData.data]);\n  return (\n    <div className=\"flex items-center gap-2 font-mono\">\n      <fetcher.Form method=\"post\">\n        <input type=\"hidden\" name=\"cacheKey\" value={cacheKey} />\n        <Button\n          variant=\"ghost\"\n          color=\"danger\"\n          onBlur={() => setDoubleCheck(false)}\n          onPress={(e) => {\n            const target = e.target as HTMLFormElement;\n            if (doubleCheck) {\n              fetcher.submit(target?.form);\n            } else {\n              setDoubleCheck(true);\n            }\n          }}\n        >\n          {fetcher.state === 'idle' ? (doubleCheck ? 'You sure?' : 'Delete') : 'Deleting...'}\n        </Button>\n      </fetcher.Form>\n      <Dialog open={IsShowDetailCache} onOpenChange={setIsShowDetailCache}>\n        <DialogTrigger asChild>\n          <Button variant=\"light\" onPress={(e) => handleOpenDetailCache(cacheKey, e)}>\n            {cacheKey}\n          </Button>\n        </DialogTrigger>\n        <DialogContent>\n          <DialogHeader>\n            <DialogTitle id=\"modal-title\">Cache detail</DialogTitle>\n          </DialogHeader>\n          {cacheValue && cacheKey ? (\n            <div className=\"flex flex-col gap-4\" id=\"modal-description\">\n              <h5>\n                Cache Key: <span>{cacheKey}</span>\n              </h5>\n              <h5>\n                Cache TTL: <span>{cacheValue.metadata.ttl}</span>\n              </h5>\n              <h5>\n                Cache Stale While Revalidate: <span>{cacheValue.metadata.swr}</span>\n              </h5>\n              <h5>\n                Cache Created At: <span>{cacheValue.metadata.createdTime}</span>\n              </h5>\n              <h5>\n                Cache Data: <span>{JSON.stringify(cacheValue.value)}</span>\n              </h5>\n            </div>\n          ) : null}\n          <DialogFooter>\n            <Button variant=\"flat\" color=\"danger\" onPress={closeHandler}>\n              Close\n            </Button>\n          </DialogFooter>\n        </DialogContent>\n      </Dialog>\n    </div>\n  );\n};\n\nconst CacheAdminRoute = () => {\n  const { cacheKeys } = useLoaderData<typeof loader>();\n  const [searchParams] = useSearchParams();\n  const location = useLocation();\n  const navigate = useNavigate();\n  const query = searchParams.get('query') ?? '';\n  const onSubmit = (value: string) => {\n    navigate(`/admin/cache?query=${value}`);\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ x: '-10%', opacity: 0 }}\n      animate={{ x: 0, opacity: 1 }}\n      exit={{ x: '-10%', opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-5\"\n    >\n      <h2>Cache Management</h2>\n      <SearchForm\n        onSubmit={onSubmit}\n        textOnButton=\"Search\"\n        textPlaceHolder=\"Search cache key\"\n        defaultValue={query}\n      />\n      <Spacer x={5} />\n      <div className=\"flex w-full flex-col gap-4\">\n        {cacheKeys.map((cacheKey) => (\n          <CacheKeyRow key={cacheKey} cacheKey={cacheKey} />\n        ))}\n      </div>\n    </motion.div>\n  );\n};\n\nexport function ErrorBoundary({ error }: { error: Error }) {\n  // eslint-disable-next-line no-console\n  console.error(error);\n\n  return <div>An unexpected error occurred: {error.message}</div>;\n}\n\nexport default CacheAdminRoute;\n"
  },
  {
    "path": "app/routes/admin+/cache_.$cacheKey.ts",
    "content": "import { json, type DataFunctionArgs } from '@remix-run/node';\nimport invariant from 'tiny-invariant';\n\nimport { authenticate } from '~/services/supabase';\nimport { lruCache } from '~/utils/server/cache.server';\n\nexport async function loader({ request, params }: DataFunctionArgs) {\n  await authenticate(request, undefined, true);\n\n  const { cacheKey } = params;\n  invariant(cacheKey, 'cacheKey is required');\n  return json({\n    cacheKey,\n    value: lruCache.get(cacheKey),\n  });\n}\n"
  },
  {
    "path": "app/routes/anime+/$animeId+/_index.tsx",
    "content": "import { useMemo } from 'react';\nimport { Card, CardBody } from '@nextui-org/card';\nimport { useParams } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { useTranslation } from 'react-i18next';\nimport { MimeType } from 'remix-image';\n\nimport type { Handle } from '~/types/handle';\nimport type { IMedia } from '~/types/media';\nimport type { loader as animeIdLoader } from '~/routes/anime+/$animeId';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport Image from '~/components/elements/Image';\nimport PhotoIcon from '~/assets/icons/PhotoIcon';\n\nexport const meta = mergeMeta<{}, { 'routes/anime+/$animeId': typeof animeIdLoader }>(\n  ({ params, matches }) => {\n    const animeData = matches.find((match) => match.id === 'routes/anime+/$animeId')?.data;\n    if (!animeData) {\n      return [\n        { title: 'Missing Anime' },\n        { name: 'description', content: `There is no anime with the ID: ${params.animeId}` },\n      ];\n    }\n    const { detail } = animeData;\n    const { title, description } = detail || {};\n    const animeTitle =\n      title?.userPreferred || title?.english || title?.romaji || title?.native || '';\n    return [\n      { title: `Sora - ${animeTitle}` },\n      {\n        name: 'description',\n        content: description\n          ? description?.replace(/<\\/?[^>]+(>|$)/g, '')\n          : `Watch ${animeTitle} in Sora`,\n      },\n      { property: 'og:url', content: `https://sorachill.vercel.app/anime/${params.animeId}` },\n      { property: 'og:title', content: `Sora - ${animeTitle}` },\n      {\n        property: 'og:description',\n        content: description\n          ? description?.replace(/<\\/?[^>]+(>|$)/g, '')\n          : `Watch ${animeTitle} in Sora`,\n      },\n      { name: 'twitter:title', content: `Sora - ${animeTitle}` },\n      {\n        name: 'twitter:description',\n        content: description\n          ? description?.replace(/<\\/?[^>]+(>|$)/g, '')\n          : `Watch ${animeTitle} in Sora`,\n      },\n    ];\n  },\n);\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/anime/${match.params.animeId}/`}\n      key={`anime-${match.params.animeId}-overview`}\n    >\n      {t('overview')}\n    </BreadcrumbItem>\n  ),\n};\n\nconst AnimeOverview = () => {\n  const animeData = useTypedRouteLoaderData('routes/anime+/$animeId');\n  const detail = animeData && animeData.detail;\n  const { animeId } = useParams();\n  const { t } = useTranslation();\n  const listRelations = useMemo(() => {\n    if (!detail?.relations || detail?.relations.length === 0) return [];\n    const listFiltered = detail?.relations.filter(\n      (relation) => relation.relationType === 'SEQUEL' || relation.relationType === 'PREQUEL',\n    );\n    const listFormatted = listFiltered.map((relation) => ({\n      id: relation.id,\n      title: relation.title,\n      posterPath: relation.image,\n      backdropPath: relation.cover,\n      voteAverage: relation.rating,\n    }));\n    return listFormatted;\n  }, [detail?.relations]);\n  return (\n    <div className=\"mt-3 flex w-full max-w-[1920px] flex-col gap-x-0 gap-y-4 px-3 sm:flex-row sm:items-stretch sm:justify-center sm:gap-x-4 sm:gap-y-0 sm:px-3.5 xl:px-4 2xl:px-5\">\n      <div className=\"flex w-full grow-0 flex-col sm:w-1/3 sm:items-center sm:justify-start\">\n        <div className=\"flex w-full flex-col items-start justify-center gap-y-4 rounded-large bg-content1 p-4 nextui-sm:w-3/4 xl:w-1/2\">\n          {detail?.nextAiringEpisode ? (\n            <div className=\"flex w-full flex-row items-center justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n              <h6 className=\"grow-0 basis-1/3\">{t('airing')}</h6>\n              <p className=\"grow\">\n                {`Ep${detail?.nextAiringEpisode?.episode}: ${detail?.nextAiringEpisode?.timeUntilAiring}`}\n              </p>\n            </div>\n          ) : null}\n          {detail?.totalEpisodes ? (\n            <div className=\"flex w-full flex-row items-center justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n              <h6 className=\"grow-0 basis-1/3\">{t('episodes')}</h6>\n              <p className=\"grow\">{detail?.totalEpisodes}</p>\n            </div>\n          ) : null}\n          {detail?.duration ? (\n            <div className=\"flex w-full flex-row items-center justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n              <h6 className=\"grow-0 basis-1/3\">{t('episode-duration')}</h6>\n              <p className=\"grow\">{detail?.duration}</p>\n            </div>\n          ) : null}\n          {detail?.status ? (\n            <div className=\"flex w-full flex-row items-center justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n              <h6 className=\"grow-0 basis-1/3\">{t('status')}</h6>\n              <p className=\"grow\">{detail?.status}</p>\n            </div>\n          ) : null}\n          {detail?.startDate ? (\n            <div className=\"flex w-full flex-row items-center justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n              <h6 className=\"grow-0 basis-1/3\">{t('start-date')}</h6>\n              <p className=\"grow\">\n                {`${detail?.startDate?.day}/${detail?.startDate?.month}/${detail?.startDate?.year}`}\n              </p>\n            </div>\n          ) : null}\n          {detail?.endDate ? (\n            <div className=\"flex w-full flex-row items-center justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n              <h6 className=\"grow-0 basis-1/3\">{t('end-date')}</h6>\n              <p className=\"grow\">\n                {`${detail?.endDate?.day}/${detail?.endDate?.month}/${detail?.endDate?.year}`}\n              </p>\n            </div>\n          ) : null}\n          {detail?.countryOfOrigin ? (\n            <div className=\"flex w-full flex-row items-center justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n              <h6 className=\"grow-0 basis-1/3\">{t('country-origin')}</h6>\n              <p className=\"grow\">{detail?.countryOfOrigin}</p>\n            </div>\n          ) : null}\n          {detail?.popularity ? (\n            <div className=\"flex w-full flex-row items-center justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n              <h6 className=\"grow-0 basis-1/3\">{t('popularity')}</h6>\n              <p className=\"grow\">{detail?.popularity}</p>\n            </div>\n          ) : null}\n          {detail?.studios ? (\n            <div className=\"flex w-full flex-row items-start justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n              <h6 className=\"grow-0 basis-1/3\">{t('studios')}</h6>\n              <div className=\"flex grow flex-col\">\n                {detail.studios.length > 0 &&\n                  detail.studios.map((studio) => <p key={studio}>{studio}</p>)}\n              </div>\n            </div>\n          ) : null}\n          {detail?.synonyms ? (\n            <div className=\"flex w-full flex-row items-start justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n              <h6 className=\"grow-0 basis-1/3\">{t('synonyms')}</h6>\n              <div className=\"flex grow flex-col\">\n                {detail?.synonyms.length > 0 &&\n                  detail?.synonyms.map((synonym) => <p key={synonym}>{synonym}</p>)}\n              </div>\n            </div>\n          ) : null}\n        </div>\n      </div>\n      <div className=\"flex w-full flex-col sm:w-2/3\">\n        <div className=\"flex flex-col items-start justify-start gap-y-4 rounded-large bg-content1 p-4\">\n          <p\n            className=\"text-justify\"\n            dangerouslySetInnerHTML={{ __html: detail?.description || '' }}\n          />\n        </div>\n        {listRelations && listRelations.length > 0 ? (\n          <MediaList\n            items={listRelations as IMedia[]}\n            itemsType=\"anime\"\n            key={`anime-relations-${animeId}`}\n            listName={t('relations')}\n            listType=\"slider-card\"\n            navigationButtons\n          />\n        ) : null}\n        {detail?.characters && detail.characters.length > 0 ? (\n          <>\n            <h2 className=\"my-5\">{t('characters')}</h2>\n            <div className=\"grid grid-cols-1 gap-4 xl:grid-cols-2\">\n              {detail.characters.slice(0, 12).map((character) => (\n                <Card\n                  key={character.id}\n                  isHoverable\n                  isPressable\n                  className=\"max-h-[80px] data-[hover=true]:ring-2 data-[hover=true]:ring-focus\"\n                >\n                  <CardBody className=\"flex h-full flex-row flex-nowrap items-center justify-start overflow-hidden p-0\">\n                    <div className=\"flex h-full grow justify-start gap-x-2\">\n                      {character?.image ? (\n                        <Image\n                          radius=\"lg\"\n                          src={character.image}\n                          width=\"60px\"\n                          height=\"80px\"\n                          alt={character?.name?.full}\n                          title={character?.name?.full}\n                          placeholder=\"empty\"\n                          classNames={{\n                            img: 'max-h-[80px]',\n                          }}\n                          options={{\n                            contentType: MimeType.WEBP,\n                          }}\n                          responsive={[\n                            {\n                              size: {\n                                width: 60,\n                                height: 80,\n                              },\n                            },\n                          ]}\n                        />\n                      ) : (\n                        <div className=\"z-0 flex min-h-[80px] min-w-[60px] basis-[60px] items-center justify-center rounded-large bg-default\">\n                          <PhotoIcon width={36} height={36} />\n                        </div>\n                      )}\n                      <div className=\"flex flex-col items-start justify-center p-1\">\n                        <h5>{character.name?.full}</h5>\n                        <p className=\"opacity-80\">{character.role}</p>\n                      </div>\n                    </div>\n                    <div className=\"flex h-full grow flex-row justify-end gap-x-2\">\n                      {character?.voiceActors && character?.voiceActors.length > 0 && (\n                        <div className=\"flex flex-col items-end justify-center p-1\">\n                          <h5>{character.voiceActors[0].name?.full}</h5>\n                          <p className=\"opacity-80\">{t('japanese')}</p>\n                        </div>\n                      )}\n                      {character?.voiceActors && character?.voiceActors[0]?.image ? (\n                        <Image\n                          src={character.voiceActors[0]?.image}\n                          width=\"60px\"\n                          height=\"80px\"\n                          alt={character.voiceActors[0].name?.full}\n                          title={character.voiceActors[0].name?.full}\n                          placeholder=\"empty\"\n                          classNames={{\n                            img: 'max-h-[80px]',\n                          }}\n                          options={{\n                            contentType: MimeType.WEBP,\n                          }}\n                          responsive={[\n                            {\n                              size: {\n                                width: 60,\n                                height: 80,\n                              },\n                            },\n                          ]}\n                        />\n                      ) : (\n                        <div className=\"z-0 flex min-h-[80px] min-w-[60px] basis-[60px] items-center justify-center rounded-large bg-default\">\n                          <PhotoIcon width={36} height={36} />\n                        </div>\n                      )}\n                    </div>\n                  </CardBody>\n                </Card>\n              ))}\n            </div>\n          </>\n        ) : null}\n        {detail?.recommendations && detail?.recommendations.length > 0 ? (\n          <MediaList\n            items={detail?.recommendations as IMedia[]}\n            itemsType=\"anime\"\n            key={`anime-recommendations-${animeId}`}\n            listName={t('recommendations')}\n            listType=\"slider-card\"\n            navigationButtons\n          />\n        ) : null}\n      </div>\n    </div>\n  );\n};\n\nexport default AnimeOverview;\n"
  },
  {
    "path": "app/routes/anime+/$animeId+/characters.tsx",
    "content": "const CharactersPage = () => (\n  <div className=\"flex w-full flex-col items-center justify-center px-3 sm:px-5\">\n    <h4>In development</h4>\n  </div>\n);\n\nexport default CharactersPage;\n"
  },
  {
    "path": "app/routes/anime+/$animeId+/episodes.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as animeIdLoader } from '~/routes/anime+/$animeId';\nimport { getAnimeEpisodeInfo } from '~/services/consumet/anilist/anilist.server';\nimport type { IAnimeInfo } from '~/services/consumet/anilist/anilist.types';\nimport { authenticate } from '~/services/supabase';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport ListEpisodes from '~/components/elements/shared/ListEpisodes';\n\nexport const loader = async ({ params, request }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const { animeId } = params;\n  if (!animeId) throw new Response('Not Found', { status: 404 });\n\n  const episodes = await getAnimeEpisodeInfo(animeId);\n  if (!episodes) throw new Response('Not Found', { status: 404 });\n  return json(\n    {\n      episodes: episodes.sort((a, b) => a.number - b.number),\n    },\n    { headers: { 'Cache-Control': CACHE_CONTROL.episode } },\n  );\n};\n\nexport const meta = mergeMeta<{}, { 'routes/anime+/$animeId': typeof animeIdLoader }>(\n  ({ params, matches }) => {\n    const animeData = matches.find((match) => match.id === 'routes/anime+/$animeId')?.data;\n    if (!animeData) {\n      return [\n        { title: 'Missing Anime' },\n        { name: 'description', content: `There is no anime with the ID: ${params.animeId}` },\n      ];\n    }\n    const { detail } = animeData;\n    const { title, description } = detail || {};\n    const animeTitle =\n      title?.userPreferred || title?.english || title?.romaji || title?.native || '';\n    return [\n      { title: `Sora - ${animeTitle} - Episodes` },\n      {\n        name: 'description',\n        content: description\n          ? description?.replace(/<\\/?[^>]+(>|$)/g, '')\n          : `Watch ${animeTitle} in Sora`,\n      },\n      {\n        property: 'og:url',\n        content: `https://sorachill.vercel.app/anime/${params.animeId}/episodes`,\n      },\n      { property: 'og:title', content: `Sora - ${animeTitle} - Episodes` },\n      {\n        property: 'og:description',\n        content: description\n          ? description?.replace(/<\\/?[^>]+(>|$)/g, '')\n          : `Watch ${animeTitle} in Sora`,\n      },\n      { name: 'twitter:title', content: `Sora - ${animeTitle} - Episodes` },\n      {\n        name: 'twitter:description',\n        content: description\n          ? description?.replace(/<\\/?[^>]+(>|$)/g, '')\n          : `Watch ${animeTitle} in Sora`,\n      },\n    ];\n  },\n);\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/anime/${match.params.animeId}/episodes`}\n      key={`anime-${match.params.animeId}-episodes`}\n    >\n      {t('episodes')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch\n      ? (parentMatch.data as { detail: IAnimeInfo })?.detail?.title?.userPreferred ||\n        (parentMatch.data as { detail: IAnimeInfo })?.detail?.title?.english ||\n        (parentMatch.data as { detail: IAnimeInfo })?.detail?.title?.romaji ||\n        (parentMatch.data as { detail: IAnimeInfo })?.detail?.title?.native ||\n        ''\n      : '',\n    subtitle: t('episodes'),\n    showImage: parentMatch\n      ? (parentMatch.data as { detail: IAnimeInfo })?.detail?.image !== undefined\n      : false,\n    imageUrl: parentMatch ? (parentMatch.data as { detail: IAnimeInfo })?.detail?.image : undefined,\n  }),\n  preventScrollToTop: true,\n  disableLayoutPadding: true,\n  customHeaderBackgroundColor: true,\n  customHeaderChangeColorOnScroll: true,\n};\n\nconst EpisodesPage = () => {\n  const { episodes } = useLoaderData<typeof loader>();\n  const animeData = useTypedRouteLoaderData('routes/anime+/$animeId');\n  const detail = animeData && animeData.detail;\n\n  return (\n    <div className=\"flex w-full flex-col items-center justify-center px-3 sm:w-2/3 sm:px-5\">\n      <ListEpisodes\n        type=\"anime\"\n        id={detail?.id}\n        episodes={episodes}\n        providers={animeData?.providers || []}\n      />\n    </div>\n  );\n};\n\nexport default EpisodesPage;\n"
  },
  {
    "path": "app/routes/anime+/$animeId+/staff.tsx",
    "content": "const StaffPage = () => (\n  <div className=\"flex w-full flex-col items-center justify-center px-3 sm:px-5\">\n    <h4>In development</h4>\n  </div>\n);\n\nexport default StaffPage;\n"
  },
  {
    "path": "app/routes/anime+/$animeId.tsx",
    "content": "import { useEffect, useRef } from 'react';\nimport { useIntersectionObserver } from '@react-hookz/web';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { Outlet, useLoaderData, useLocation } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, useTransform } from 'framer-motion';\n\nimport type { Handle } from '~/types/handle';\nimport { getAnimeInfo } from '~/services/consumet/anilist/anilist.server';\nimport type { IAnimeInfo } from '~/services/consumet/anilist/anilist.types';\nimport getProviderList from '~/services/provider.server';\nimport { authenticate } from '~/services/supabase';\nimport useColorDarkenLighten from '~/utils/react/hooks/useColorDarkenLighten';\nimport { useCustomHeaderChangePosition } from '~/utils/react/hooks/useHeader';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport { useHeaderStyle } from '~/store/layout/useHeaderStyle';\nimport { useLayout } from '~/store/layout/useLayout';\nimport { animeDetailsPages } from '~/constants/tabLinks';\nimport { AnimeDetail, MediaBackgroundImage } from '~/components/media/MediaDetail';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport ErrorBoundaryView from '~/components/elements/shared/ErrorBoundaryView';\nimport TabLink from '~/components/elements/tab/TabLink';\nimport { backgroundStyles } from '~/components/styles/primitives';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const { animeId } = params;\n  const aid = Number(animeId);\n  if (!animeId) throw new Response('Not Found', { status: 404 });\n  await authenticate(request, undefined, true);\n  const detail = await getAnimeInfo(animeId);\n  if (!detail) throw new Response('Not Found', { status: 404 });\n  const title = detail.title?.english || detail.title?.userPreferred || detail.title?.romaji || '';\n  const orgTitle = detail.title?.native;\n  const year = detail.releaseDate;\n  const animeType = detail?.type?.toLowerCase() || 'tv';\n  const isEnded = detail?.status === 'FINISHED';\n  const providers = await getProviderList({\n    type: 'anime',\n    title,\n    orgTitle,\n    year,\n    season: undefined,\n    animeId: aid,\n    animeType,\n    isEnded,\n  });\n\n  if (providers && providers.length > 0) {\n    return json({ detail, providers });\n  }\n  return json(\n    {\n      detail,\n      providers: [],\n    },\n    { headers: { 'Cache-Control': CACHE_CONTROL.detail } },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader>(({ data, params }) => {\n  if (!data) {\n    return [];\n  }\n  const { detail } = data;\n  const { title } = detail || {};\n  const animeTitle = title?.userPreferred || title?.english || title?.romaji || title?.native || '';\n  return [\n    { property: 'og:image', content: `https://img.anili.st/media/${params.animeId}` },\n    {\n      name: 'keywords',\n      content: `Watch ${animeTitle}, Stream ${animeTitle}, Watch ${animeTitle} HD, Online ${animeTitle}, Streaming ${animeTitle}, English, Subtitle ${animeTitle}, English Subtitle`,\n    },\n    { name: 'twitter:image', content: `https://img.anili.st/media/${params.animeId}` },\n  ];\n});\n\nexport const handle: Handle = {\n  breadcrumb: ({ match }) => (\n    <BreadcrumbItem to={`/anime/${match.params.animeId}`} key={`anime-${match.params.animeId}`}>\n      {(match.data as { detail: IAnimeInfo })?.detail?.title?.english ||\n        (match.data as { detail: IAnimeInfo })?.detail?.title?.romaji}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ match, t }) => ({\n    title:\n      (match.data as { detail: IAnimeInfo })?.detail?.title?.userPreferred ||\n      (match.data as { detail: IAnimeInfo })?.detail?.title?.english ||\n      (match.data as { detail: IAnimeInfo })?.detail?.title?.romaji ||\n      (match.data as { detail: IAnimeInfo })?.detail?.title?.native ||\n      '',\n    subtitle: t('overview'),\n    showImage: (match.data as { detail: IAnimeInfo })?.detail?.image !== undefined,\n    imageUrl: (match.data as { detail: IAnimeInfo })?.detail?.image,\n  }),\n  preventScrollToTop: true,\n  disableLayoutPadding: true,\n  customHeaderBackgroundColor: true,\n  customHeaderChangeColorOnScroll: true,\n};\n\nconst AnimeDetailPage = () => {\n  const { detail } = useLoaderData<typeof loader>();\n  const { state } = useLocation();\n  const isHydrated = useHydrated();\n  const { backgroundColor } = useColorDarkenLighten(detail?.color);\n  const { sidebarBoxedMode } = useSoraSettings();\n  const { viewportRef, scrollY } = useLayout((scrollState) => scrollState);\n  const { setBackgroundColor, startChangeScrollPosition } = useHeaderStyle(\n    (headerState) => headerState,\n  );\n  const tabLinkRef = useRef<HTMLDivElement>(null);\n  const tablinkIntersection = useIntersectionObserver(tabLinkRef, {\n    root: viewportRef,\n    rootMargin: sidebarBoxedMode ? '-180px 0px 0px 0px' : '-165px 0px 0px 0px',\n    threshold: [0.5],\n  });\n  const paddingTop = useTransform(\n    scrollY,\n    [0, startChangeScrollPosition, startChangeScrollPosition + 100],\n    [16, 16, startChangeScrollPosition ? 0 : 16],\n  );\n  const paddingBottom = useTransform(\n    scrollY,\n    [0, startChangeScrollPosition, startChangeScrollPosition + 100],\n    [32, 32, startChangeScrollPosition ? 0 : 32],\n  );\n  useCustomHeaderChangePosition(tablinkIntersection);\n\n  useEffect(() => {\n    if (startChangeScrollPosition) {\n      setBackgroundColor(backgroundColor);\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [backgroundColor, startChangeScrollPosition]);\n\n  const currentTime = state && (state as { currentTime: number | undefined }).currentTime;\n\n  return (\n    <>\n      <MediaBackgroundImage backdropPath={detail?.cover} backgroundColor={backgroundColor} />\n      <div className=\"relative top-[-80px] w-full sm:top-[-200px]\">\n        <AnimeDetail item={detail} trailerTime={currentTime} />\n        <div className=\"flex w-full flex-col items-center justify-center\">\n          <motion.div\n            className=\"sticky top-[61px] z-[1000] flex w-full justify-center transition-[padding] duration-100 ease-in-out\"\n            style={{\n              backgroundColor: isHydrated ? backgroundColor : 'transparent',\n              paddingTop,\n              paddingBottom,\n            }}\n            ref={tabLinkRef}\n          >\n            <div\n              className={backgroundStyles({ tablink: true })}\n              style={{ backgroundColor: isHydrated ? backgroundColor : 'transparent' }}\n            />\n            <TabLink pages={animeDetailsPages} linkTo={`/anime/${detail?.id}`} />\n          </motion.div>\n          <Outlet />\n        </div>\n      </div>\n    </>\n  );\n};\n\nexport function ErrorBoundary() {\n  return (\n    <ErrorBoundaryView\n      statusHandlers={{\n        404: ({ params }) => <p>There is no anime with the ID: {params.animeId}</p>,\n      }}\n    />\n  );\n}\n\nexport default AnimeDetailPage;\n"
  },
  {
    "path": "app/routes/anime+/$animeId_.episode.$episodeId.watch.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport type { IMedia } from '~/types/media';\nimport { getAniskip, type IAniSkipResponse } from '~/services/aniskip/aniskip.server';\nimport {\n  getAnimeEpisodeInfo,\n  getAnimeEpisodeStream,\n  getAnimeInfo,\n} from '~/services/consumet/anilist/anilist.server';\nimport type { IAnimeInfo, IEpisodeInfo } from '~/services/consumet/anilist/anilist.types';\nimport { getBilibiliEpisode, getBilibiliInfo } from '~/services/consumet/bilibili/bilibili.server';\nimport {\n  getKissKhEpisodeStream,\n  getKissKhEpisodeSubtitle,\n  getKissKhInfo,\n} from '~/services/kisskh/kisskh.server';\nimport { loklokGetMovieInfo, loklokGetTvEpInfo } from '~/services/loklok';\nimport { LOKLOK_URL } from '~/services/loklok/utils.server';\nimport getProviderList from '~/services/provider.server';\nimport { authenticate, insertHistory } from '~/services/supabase';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport ErrorBoundaryView from '~/components/elements/shared/ErrorBoundaryView';\nimport WatchDetail from '~/components/elements/shared/WatchDetail';\n\nconst checkHasNextEpisode = (\n  provider: string,\n  currentEpisode: number,\n  totalEpisodes: number,\n  totalProviderEpisodes?: number,\n) => {\n  if (provider === 'Gogo' || provider === 'Zoro') {\n    return totalEpisodes > currentEpisode;\n  }\n  if (totalProviderEpisodes) {\n    return totalProviderEpisodes > currentEpisode;\n  }\n};\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const user = await authenticate(request, true, true, true);\n\n  const url = new URL(request.url);\n  const provider = url.searchParams.get('provider');\n  const idProvider = url.searchParams.get('id');\n  const isGetSkipOpEd = url.searchParams.get('skipOpEd') === 'true';\n  const routePlayer = `${url.pathname}${url.search}`;\n  const { animeId, episodeId } = params;\n  const aid = Number(animeId);\n  if (!animeId || !episodeId) throw new Response('Not Found', { status: 404 });\n\n  const [detail, episodes] = await Promise.all([\n    getAnimeInfo(animeId),\n    getAnimeEpisodeInfo(animeId),\n  ]);\n  const sortEpisodes = episodes?.sort((a, b) => a.number - b.number);\n  const title =\n    detail?.title?.english || detail?.title?.userPreferred || detail?.title?.romaji || '';\n  const orgTitle = detail?.title?.native;\n  const year = detail?.releaseDate;\n  const episodeIndex = episodes\n    ? episodes.find((e) => e.number === Number(episodeId))?.id\n    : undefined;\n  const totalEpisodes = Number(episodes?.length);\n  const episodeInfo = episodes?.find((e: IEpisodeInfo) => e.number === Number(episodeId));\n  const titlePlayer = title;\n  const posterPlayer = detail?.cover || '';\n  const trailerAnime = detail?.trailer;\n  const subtitleOptions = {\n    type: 'episode',\n    title: detail?.title?.userPreferred || detail?.title?.english || '',\n    sub_format: provider === 'KissKh' ? 'srt' : 'webvtt',\n  };\n  const animeType = detail?.type?.toLowerCase() || 'tv';\n  const overview = detail?.description;\n  const malId = detail?.malId;\n  const skipTypes = ['op', 'ed', 'mixed-ed', 'mixed-op', 'recap'];\n  const isEnded = detail?.status === 'FINISHED';\n\n  if (user) {\n    insertHistory({\n      user_id: user.id,\n      media_type: 'anime',\n      duration: (detail?.duration || 0) * 60,\n      watched: 0,\n      route: url.pathname + url.search,\n      media_id: (detail?.id || animeId).toString(),\n      poster: detail?.cover,\n      title:\n        detail?.title?.userPreferred ||\n        detail?.title?.english ||\n        detail?.title?.native ||\n        detail?.title?.romaji ||\n        undefined,\n      overview: detail?.description,\n      season: detail?.season,\n      episode: episodeId,\n    });\n  }\n\n  const highlights: {\n    start: number;\n    end: number;\n    text: string;\n  }[] = [];\n  const getHighlights = async (aniskip: IAniSkipResponse) => {\n    if (aniskip.statusCode === 200) {\n      const { results } = aniskip;\n      // eslint-disable-next-line no-restricted-syntax\n      for (const skipType of skipTypes) {\n        const item = results.find((result) => result.skipType === skipType);\n        if (item) {\n          highlights.push({\n            start: item?.interval?.startTime,\n            end: item?.interval?.endTime,\n            text: item?.skipType.toUpperCase(),\n          });\n        }\n      }\n      // sort highlights by start time\n      highlights.sort((a, b) => a.start - b.start);\n    }\n  };\n\n  if (provider === 'Loklok') {\n    if (!idProvider) throw new Response('Id Not Found', { status: 404 });\n    const [tvDetail, providers, aniskip] = await Promise.all([\n      detail?.type === 'MOVIE'\n        ? loklokGetMovieInfo(idProvider)\n        : loklokGetTvEpInfo(idProvider, Number(episodeId) - 1),\n      getProviderList({\n        type: 'anime',\n        title,\n        orgTitle,\n        year,\n        season: undefined,\n        animeId: aid,\n        animeType,\n        isEnded,\n      }),\n      malId && isGetSkipOpEd ? getAniskip(malId, Number(episodeId)) : undefined,\n    ]);\n    const totalProviderEpisodes = Number(tvDetail?.data?.episodeCount);\n    const hasNextEpisode = checkHasNextEpisode(\n      provider,\n      Number(episodeInfo?.number),\n      totalEpisodes,\n      totalProviderEpisodes,\n    );\n    if (aniskip) {\n      await getHighlights(aniskip);\n      return json(\n        {\n          provider,\n          idProvider,\n          detail,\n          episodes: sortEpisodes,\n          hasNextEpisode,\n          sources: tvDetail?.sources,\n          subtitles: tvDetail?.subtitles.map((sub) => ({\n            lang: `${sub.language} (${sub.lang})`,\n            url: `${LOKLOK_URL}/subtitle?url=${sub.url}`,\n          })),\n          userId: user?.id,\n          episodeInfo,\n          providers,\n          routePlayer,\n          titlePlayer,\n          id: aid,\n          posterPlayer,\n          typeVideo: 'anime',\n          trailerAnime,\n          subtitleOptions,\n          overview,\n          highlights,\n        },\n        {\n          headers: {\n            'Cache-Control': CACHE_CONTROL.detail,\n          },\n        },\n      );\n    }\n    return json(\n      {\n        provider,\n        idProvider,\n        detail,\n        episodes: sortEpisodes,\n        hasNextEpisode,\n        sources: tvDetail?.sources,\n        subtitles: tvDetail?.subtitles.map((sub) => ({\n          lang: `${sub.language} (${sub.lang})`,\n          url: `${LOKLOK_URL}/subtitle?url=${sub.url}`,\n        })),\n        userId: user?.id,\n        episodeInfo,\n        providers,\n        routePlayer,\n        titlePlayer,\n        id: aid,\n        posterPlayer,\n        typeVideo: 'anime',\n        trailerAnime,\n        subtitleOptions,\n        overview,\n      },\n      {\n        headers: {\n          'Cache-Control': CACHE_CONTROL.detail,\n        },\n      },\n    );\n  }\n\n  if (provider === 'Gogo') {\n    const [providers, aniskip, gogoEpisodes] = await Promise.all([\n      getProviderList({\n        type: 'anime',\n        title,\n        orgTitle,\n        year,\n        season: undefined,\n        animeId: aid,\n        animeType,\n        isEnded,\n      }),\n      malId && isGetSkipOpEd ? getAniskip(malId, Number(episodeId)) : undefined,\n      getAnimeEpisodeInfo(animeId, undefined, 'gogoanime'),\n    ]);\n    const hasNextEpisode = checkHasNextEpisode(\n      provider,\n      Number(episodeInfo?.number),\n      totalEpisodes,\n    );\n    const gogoEpisodeId = gogoEpisodes\n      ? gogoEpisodes.find((e) => e.number === Number(episodeId))?.id\n      : undefined;\n    if (aniskip) {\n      const [, episodeDetail] = await Promise.all([\n        getHighlights(aniskip),\n        getAnimeEpisodeStream(gogoEpisodeId, 'gogoanime'),\n      ]);\n      return json(\n        {\n          provider,\n          detail,\n          episodes: sortEpisodes,\n          hasNextEpisode,\n          sources: episodeDetail?.sources.map((source) => ({\n            ...source,\n            url: `${\n              process.env.CORS_PROXY_URL === undefined\n                ? source.url\n                : `${process.env.CORS_PROXY_URL}?url=${encodeURIComponent(source.url)}`\n            }`,\n          })),\n          userId: user?.id,\n          episodeInfo,\n          providers,\n          routePlayer,\n          titlePlayer,\n          id: aid,\n          posterPlayer,\n          typeVideo: 'anime',\n          trailerAnime,\n          subtitleOptions,\n          overview,\n          highlights,\n        },\n        {\n          headers: {\n            'Cache-Control': CACHE_CONTROL.detail,\n          },\n        },\n      );\n    }\n    const episodeDetail = await getAnimeEpisodeStream(gogoEpisodeId, 'gogoanime');\n    return json(\n      {\n        provider,\n        detail,\n        episodes: sortEpisodes,\n        hasNextEpisode,\n        sources: episodeDetail?.sources.map((source) => ({\n          ...source,\n          url: `${\n            process.env.CORS_PROXY_URL === undefined\n              ? source.url\n              : `${process.env.CORS_PROXY_URL}?url=${encodeURIComponent(source.url)}`\n          }`,\n        })),\n        userId: user?.id,\n        episodeInfo,\n        providers,\n        routePlayer,\n        titlePlayer,\n        id: aid,\n        posterPlayer,\n        typeVideo: 'anime',\n        trailerAnime,\n        subtitleOptions,\n        overview,\n      },\n      {\n        headers: {\n          'Cache-Control': CACHE_CONTROL.detail,\n        },\n      },\n    );\n  }\n\n  if (provider === 'Zoro') {\n    const [providers, aniskip, zoroEpisodes] = await Promise.all([\n      getProviderList({\n        type: 'anime',\n        title,\n        orgTitle,\n        year,\n        season: undefined,\n        animeId: aid,\n        animeType,\n        isEnded,\n      }),\n      malId && isGetSkipOpEd ? getAniskip(malId, Number(episodeId)) : undefined,\n      getAnimeEpisodeInfo(animeId, undefined, 'zoro'),\n    ]);\n    const hasNextEpisode = checkHasNextEpisode(\n      provider,\n      Number(episodeInfo?.number),\n      totalEpisodes,\n    );\n    const zoroEpisodeId = zoroEpisodes\n      ? zoroEpisodes.find((e) => e.number === Number(episodeId))?.id\n      : undefined;\n    if (aniskip) {\n      const [, episodeDetail] = await Promise.all([\n        getHighlights(aniskip),\n        getAnimeEpisodeStream(zoroEpisodeId, 'zoro', 'vidstreaming'),\n      ]);\n      return json(\n        {\n          provider,\n          detail,\n          episodes: sortEpisodes,\n          hasNextEpisode,\n          sources: episodeDetail?.sources.map((source) => ({\n            ...source,\n            url: `${\n              process.env.CORS_PROXY_URL === undefined\n                ? source.url\n                : `${process.env.CORS_PROXY_URL}?url=${encodeURIComponent(source.url)}`\n            }`,\n          })),\n          userId: user?.id,\n          episodeInfo,\n          providers,\n          routePlayer,\n          titlePlayer,\n          id: aid,\n          posterPlayer,\n          typeVideo: 'anime',\n          trailerAnime,\n          subtitleOptions,\n          overview,\n\n          highlights,\n        },\n        {\n          headers: {\n            'Cache-Control': CACHE_CONTROL.detail,\n          },\n        },\n      );\n    }\n    const episodeDetail = await getAnimeEpisodeStream(zoroEpisodeId, 'zoro', 'vidstreaming');\n    return json(\n      {\n        provider,\n        detail,\n        episodes: sortEpisodes,\n        hasNextEpisode,\n        sources: episodeDetail?.sources.map((source) => ({\n          ...source,\n          url: `${\n            process.env.CORS_PROXY_URL === undefined\n              ? source.url\n              : `${process.env.CORS_PROXY_URL}?url=${encodeURIComponent(source.url)}`\n          }`,\n        })),\n        userId: user?.id,\n        episodeInfo,\n        providers,\n        routePlayer,\n        titlePlayer,\n        id: aid,\n        posterPlayer,\n        typeVideo: 'anime',\n        trailerAnime,\n        subtitleOptions,\n        overview,\n      },\n      {\n        headers: {\n          'Cache-Control': CACHE_CONTROL.detail,\n        },\n      },\n    );\n  }\n\n  if (provider === 'Bilibili') {\n    if (!idProvider) throw new Response('Id Not Found', { status: 404 });\n    const [animeInfo, providers, aniskip] = await Promise.all([\n      getBilibiliInfo(Number(idProvider)),\n      getProviderList({\n        type: 'anime',\n        title,\n        orgTitle,\n        year,\n        season: undefined,\n        animeId: aid,\n        animeType,\n        isEnded,\n      }),\n      malId && isGetSkipOpEd ? getAniskip(malId, Number(episodeId)) : undefined,\n    ]);\n    const episodeSearch = animeInfo?.episodes?.find((e) => e?.number === Number(episodeId));\n    const episodeDetail = await getBilibiliEpisode(Number(episodeSearch?.id));\n    const totalProviderEpisodes = Number(animeInfo?.totalEpisodes);\n    const hasNextEpisode = checkHasNextEpisode(\n      provider,\n      Number(episodeId),\n      totalEpisodes,\n      totalProviderEpisodes,\n    );\n    if (aniskip) {\n      await getHighlights(aniskip);\n      return json(\n        {\n          provider,\n          idProvider,\n          detail,\n          episodes: sortEpisodes,\n          hasNextEpisode,\n          sources: [\n            {\n              url: episodeDetail?.sources[0]?.file || '',\n              isDASH: episodeDetail?.sources[0]?.type === 'dash',\n              quality: 'auto',\n            },\n          ],\n          subtitles: episodeDetail?.subtitles.map((sub) => ({\n            lang: `${sub.language} (${sub.lang})`,\n            url: sub.file,\n          })),\n          userId: user?.id,\n          episodeInfo,\n          providers,\n          routePlayer,\n          titlePlayer,\n          id: aid,\n          posterPlayer,\n          typeVideo: 'anime',\n          trailerAnime,\n          subtitleOptions,\n          overview,\n\n          highlights,\n        },\n        {\n          headers: {\n            'Cache-Control': CACHE_CONTROL.detail,\n          },\n        },\n      );\n    }\n\n    return json(\n      {\n        provider,\n        idProvider,\n        detail,\n        episodes: sortEpisodes,\n        hasNextEpisode,\n        sources: [\n          {\n            url: episodeDetail?.sources[0]?.file || '',\n            isDASH: episodeDetail?.sources[0]?.type === 'dash',\n            quality: 'auto',\n          },\n        ],\n        subtitles: episodeDetail?.subtitles.map((sub) => ({\n          lang: `${sub.language} (${sub.lang})`,\n          url: sub.file,\n        })),\n        userId: user?.id,\n        episodeInfo,\n        providers,\n        routePlayer,\n        titlePlayer,\n        id: aid,\n        posterPlayer,\n        typeVideo: 'anime',\n        trailerAnime,\n        subtitleOptions,\n        overview,\n      },\n      {\n        headers: {\n          'Cache-Control': CACHE_CONTROL.detail,\n        },\n      },\n    );\n  }\n\n  if (provider === 'KissKh') {\n    if (!idProvider) throw new Response('Id Not Found', { status: 404 });\n    const [episodeDetail, providers, aniskip] = await Promise.all([\n      getKissKhInfo(Number(idProvider)),\n      getProviderList({\n        type: 'anime',\n        title,\n        orgTitle,\n        year,\n        season: undefined,\n        animeId: aid,\n        animeType,\n        isEnded,\n      }),\n      malId && isGetSkipOpEd ? getAniskip(malId, Number(episodeId)) : undefined,\n    ]);\n\n    const totalProviderEpisodes = Number(episodeDetail?.episodes?.length);\n    const hasNextEpisode = checkHasNextEpisode(\n      provider,\n      Number(episodeId),\n      totalEpisodes,\n      totalProviderEpisodes,\n    );\n    const episodeSearch = episodeDetail?.episodes?.find((e) => e?.number === Number(episodeId));\n    const [episodeStream, episodeSubtitle] = await Promise.all([\n      getKissKhEpisodeStream(Number(episodeSearch?.id)),\n      episodeSearch && episodeSearch.sub > 0\n        ? getKissKhEpisodeSubtitle(Number(episodeSearch?.id))\n        : undefined,\n    ]);\n\n    if (aniskip) {\n      await getHighlights(aniskip);\n      return json(\n        {\n          provider,\n          idProvider,\n          detail,\n          episodes: sortEpisodes,\n          hasNextEpisode,\n          sources: [{ url: episodeStream?.Video || '', isM3U8: true, quality: 'auto' }],\n          subtitles: episodeSubtitle?.map((sub) => ({\n            lang: sub.label,\n            url: sub.src,\n            ...(sub.default && { default: true }),\n          })),\n          userId: user?.id,\n          episodeInfo,\n          providers,\n          routePlayer,\n          titlePlayer,\n          id: aid,\n          posterPlayer,\n          typeVideo: 'anime',\n          trailerAnime,\n          subtitleOptions,\n          overview,\n\n          highlights,\n        },\n        {\n          headers: {\n            'Cache-Control': CACHE_CONTROL.detail,\n          },\n        },\n      );\n    }\n\n    return json(\n      {\n        provider,\n        idProvider,\n        detail,\n        episodes: sortEpisodes,\n        hasNextEpisode,\n        sources: [{ url: episodeStream?.Video || '', isM3U8: true, quality: 'auto' }],\n        subtitles: episodeSubtitle?.map((sub) => ({\n          lang: sub.label,\n          url: sub.src,\n          ...(sub.default && { default: true }),\n        })),\n        userId: user?.id,\n        episodeInfo,\n        providers,\n        routePlayer,\n        titlePlayer,\n        id: aid,\n        posterPlayer,\n        typeVideo: 'anime',\n        trailerAnime,\n        subtitleOptions,\n        overview,\n      },\n      {\n        headers: {\n          'Cache-Control': CACHE_CONTROL.detail,\n        },\n      },\n    );\n  }\n  const [sources, providers, aniskip] = await Promise.all([\n    getAnimeEpisodeStream(episodeIndex),\n    getProviderList({\n      type: 'anime',\n      title,\n      orgTitle,\n      year,\n      season: undefined,\n      animeId: aid,\n      animeType,\n      isEnded,\n    }),\n    malId && isGetSkipOpEd ? getAniskip(malId, Number(episodeId)) : undefined,\n  ]);\n\n  if (!detail || !sources) throw new Response('Not Found', { status: 404 });\n  if (aniskip) {\n    await getHighlights(aniskip);\n    return json(\n      {\n        detail,\n        episodes: sortEpisodes,\n        sources: sources?.sources.map((source) => ({\n          ...source,\n          url: `${\n            process.env.CORS_PROXY_URL === undefined\n              ? source.url\n              : `${process.env.CORS_PROXY_URL}?url=${encodeURIComponent(source.url)}`\n          }`,\n        })),\n        userId: user?.id,\n        episodeInfo,\n        providers,\n        routePlayer,\n        titlePlayer,\n        id: aid,\n        posterPlayer,\n        typeVideo: 'anime',\n        trailerAnime,\n        subtitleOptions,\n        overview,\n        highlights,\n      },\n      {\n        headers: {\n          'Cache-Control': CACHE_CONTROL.detail,\n        },\n      },\n    );\n  }\n\n  return json(\n    {\n      detail,\n      episodes: sortEpisodes,\n      sources: sources?.sources.map((source) => ({\n        ...source,\n        url: `${\n          process.env.CORS_PROXY_URL === undefined\n            ? source.url\n            : `${process.env.CORS_PROXY_URL}?url=${encodeURIComponent(source.url)}`\n        }`,\n      })),\n      userId: user?.id,\n      episodeInfo,\n      providers,\n      routePlayer,\n      titlePlayer,\n      id: aid,\n      posterPlayer,\n      typeVideo: 'anime',\n      trailerAnime,\n      subtitleOptions,\n      overview,\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.detail,\n      },\n    },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader>(({ data, params }) => {\n  if (!data) {\n    return [\n      { title: 'Missing Episode' },\n      { name: 'description', content: `This anime doesn't has episode ${params.episodeId}` },\n    ];\n  }\n  const { detail, episodeInfo } = data;\n  const { title, description } = detail || {};\n  const animeTitle = title?.userPreferred || title?.english || title?.romaji || title?.native || '';\n  return [\n    { title: `Sora - Watch ${animeTitle} episode ${episodeInfo?.number || ''}` },\n    {\n      name: 'description',\n      content: description\n        ? description?.replace(/<\\/?[^>]+(>|$)/g, '')\n        : `Watch ${animeTitle} in Sora`,\n    },\n    {\n      property: 'og:url',\n      content: `https://sorachill.vercel.app/anime/${params.animeId}/episode/${params.episodeId}/watch`,\n    },\n    {\n      property: 'og:title',\n\n      content: `Sora - Watch ${animeTitle} episode ${episodeInfo?.number || ''}`,\n    },\n    {\n      property: 'og:description',\n      content: description\n        ? description?.replace(/<\\/?[^>]+(>|$)/g, '')\n        : `Watch ${animeTitle} in Sora`,\n    },\n    { property: 'og:image', content: `https://img.anili.st/media/${params.animeId}` },\n    {\n      name: 'keywords',\n      content: `Watch ${animeTitle}, Stream ${animeTitle}, Watch ${animeTitle} HD, Online ${animeTitle}, Streaming ${animeTitle}, English, Subtitle ${animeTitle}, English Subtitle`,\n    },\n    { name: 'twitter:image', content: `https://img.anili.st/media/${params.animeId}` },\n    {\n      name: 'twitter:title',\n\n      content: `Sora - Watch ${animeTitle} episode ${episodeInfo?.number || ''}`,\n    },\n    {\n      name: 'twitter:description',\n      content: description\n        ? description?.replace(/<\\/?[^>]+(>|$)/g, '')\n        : `Watch ${animeTitle} in Sora`,\n    },\n  ];\n});\n\nexport const handle: Handle = {\n  breadcrumb: ({ match }) => (\n    <>\n      <BreadcrumbItem\n        to={`/anime/${match.params.animeId}/`}\n        key={`anime-${match.params.animeId}-overview`}\n      >\n        {(match.data as { detail: IAnimeInfo })?.detail?.title?.english ||\n          (match.data as { detail: IAnimeInfo })?.detail?.title?.romaji}\n      </BreadcrumbItem>\n      <BreadcrumbItem\n        to={`/anime/${match.params.animeId}/episode/${match.params.episodeId}`}\n        key={`anime-${match.params.animeId}-episode-${match.params.episodeId}`}\n      >\n        {(match?.data as { episodeInfo: IEpisodeInfo })?.episodeInfo?.title ||\n          match.params.episodeId}\n      </BreadcrumbItem>\n    </>\n  ),\n  miniTitle: ({ match, t }) => ({\n    title:\n      (match.data as { detail: IAnimeInfo })?.detail?.title?.userPreferred ||\n      (match.data as { detail: IAnimeInfo })?.detail?.title?.english ||\n      (match.data as { detail: IAnimeInfo })?.detail?.title?.romaji ||\n      (match.data as { detail: IAnimeInfo })?.detail?.title?.native ||\n      '',\n    subtitle: `${t('episode')} ${match.params.episodeId}`,\n    showImage: (match.data as { detail: IAnimeInfo })?.detail?.image !== undefined,\n    imageUrl: (match.data as { detail: IAnimeInfo })?.detail?.image,\n  }),\n  playerSettings: {\n    isMini: false,\n    shouldShowPlayer: true,\n  },\n};\n\nconst AnimeEpisodeWatch = () => {\n  const { detail, episodes, providers } = useLoaderData<typeof loader>();\n  return (\n    <div className=\"mt-3 flex w-full flex-col items-center justify-center px-3 sm:px-0\">\n      <WatchDetail\n        type=\"anime\"\n        id={detail?.id}\n        episodes={episodes}\n        title={detail?.title?.english || ''}\n        overview={detail?.description || ''}\n        posterPath={detail?.image}\n        anilistRating={detail?.rating}\n        genresAnime={detail?.genres}\n        recommendationsAnime={detail?.recommendations as IMedia[]}\n        color={detail?.color}\n        providers={providers}\n      />\n    </div>\n  );\n};\n\nexport function ErrorBoundary() {\n  return (\n    <ErrorBoundaryView\n      statusHandlers={{\n        404: ({ params }) => <p>This anime doesn't has episode {params.episodeId}</p>,\n      }}\n    />\n  );\n}\n\nexport default AnimeEpisodeWatch;\n"
  },
  {
    "path": "app/routes/anime+/_index.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion } from 'framer-motion';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport type { IMedia } from '~/types/media';\nimport {\n  getAnimePopular,\n  getAnimeRecentEpisodes,\n  getAnimeTrending,\n} from '~/services/consumet/anilist/anilist.server';\nimport { authenticate } from '~/services/supabase';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page'));\n  if (!page || page < 1 || page > 1000) page = 1;\n\n  const [trendingAnime, popularAnime, recentEpisodes] = await Promise.all([\n    getAnimeTrending(page, 10),\n    getAnimePopular(page, 20),\n    getAnimeRecentEpisodes('gogoanime', page, 20),\n  ]);\n\n  return json(\n    {\n      trending: trendingAnime,\n      popular: popularAnime,\n      recentEpisodes,\n    },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.anime },\n    },\n  );\n};\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Anime' },\n  { name: 'description', content: 'Discover anime in Sora' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/anime' },\n  { property: 'og:title', content: 'Sora - Anime' },\n  { property: 'og:description', content: 'Discover anime in Sora' },\n  { name: 'twitter:title', content: 'Sora - Anime' },\n  { name: 'twitter:description', content: 'Discover anime in Sora' },\n]);\n\nexport const handle: Handle = {\n  disableLayoutPadding: true,\n  miniTitle: ({ t }) => ({\n    title: t('anime'),\n    showImage: false,\n  }),\n};\n\nconst AnimePage = () => {\n  const { trending, popular, recentEpisodes } = useLoaderData<typeof loader>() || {};\n  const location = useLocation();\n  const navigate = useNavigate();\n  const { t } = useTranslation();\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ x: '-10%', opacity: 0 }}\n      animate={{ x: '0', opacity: 1 }}\n      exit={{ y: '-10%', opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      style={{\n        width: '100%',\n        display: 'flex',\n        justifyContent: 'center',\n        flexDirection: 'column',\n        alignItems: 'center',\n      }}\n    >\n      {trending && trending.results && trending.results.length > 0 && (\n        <MediaList listType=\"slider-banner\" items={trending?.results as IMedia[]} />\n      )}\n      <div className=\"mt-9 flex w-full flex-col items-center justify-start px-3 sm:px-5\">\n        {popular && popular.results && popular.results.length > 0 && (\n          <MediaList\n            items={popular.results as IMedia[]}\n            itemsType=\"anime\"\n            listName={t('popular-anime')}\n            listType=\"slider-card\"\n            navigationButtons\n            onClickViewMore={() => navigate('/anime/popular')}\n            showMoreList\n          />\n        )}\n        {recentEpisodes && recentEpisodes.results && recentEpisodes.results.length > 0 && (\n          <MediaList\n            items={recentEpisodes.results as IMedia[]}\n            itemsType=\"episode\"\n            listName={t('recent-episodes')}\n            listType=\"slider-card\"\n            navigationButtons\n            onClickViewMore={() => navigate('/anime/recent-episodes')}\n            provider=\"gogoanime\"\n            showMoreList\n          />\n        )}\n      </div>\n    </motion.div>\n  );\n};\n\nexport default AnimePage;\n"
  },
  {
    "path": "app/routes/anime+/popular.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport type { IMedia } from '~/types/media';\nimport { getAnimePopular } from '~/services/consumet/anilist/anilist.server';\nimport { authenticate } from '~/services/supabase';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Popular Anime' },\n  { name: 'description', content: 'Popular Anime' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/anime/popular' },\n  { property: 'og:title', content: 'Sora - Popular Anime' },\n  { property: 'og:description', content: 'Popular Anime' },\n  { name: 'twitter:title', content: 'Sora - Popular Anime' },\n  { name: 'twitter:description', content: 'Popular Anime' },\n]);\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page'));\n  if (!page && (page < 1 || page > 1000)) page = 1;\n\n  return json(\n    {\n      items: await getAnimePopular(page, 20),\n    },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.popular },\n    },\n  );\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/anime/popular\" key=\"anime-popular\">\n      {t('popular-anime')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('anime'),\n    subtitle: t('popular'),\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst PopularAnime = () => {\n  const { items } = useLoaderData<typeof loader>();\n  const location = useLocation();\n  const navigate = useNavigate();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      return;\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate('/anime/trending');\n    }\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <MediaList\n        currentPage={items?.currentPage || 1}\n        hasNextPage={items?.hasNextPage || false}\n        items={items?.results as IMedia[]}\n        itemsType=\"anime\"\n        listName={t('popular-anime')}\n        listType=\"grid\"\n        showListTypeChangeButton\n      />\n    </motion.div>\n  );\n};\n\nexport default PopularAnime;\n"
  },
  {
    "path": "app/routes/anime+/random.tsx",
    "content": "import { redirect, type LoaderFunctionArgs } from '@remix-run/node';\n\nimport { getAnimeRandom } from '~/services/consumet/anilist/anilist.server';\nimport { authenticate } from '~/services/supabase';\nimport { redirectWithToast } from '~/utils/server/toast-session.server';\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const randomAnime = await getAnimeRandom();\n  if (randomAnime) {\n    return redirect(`/anime/${randomAnime.id}/`);\n  }\n  return redirectWithToast(request, '/anime/popular', {\n    type: 'error',\n    title: 'Error',\n    description: 'Could not find a random anime',\n  });\n};\n"
  },
  {
    "path": "app/routes/anime+/recent-episodes.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport type { IMedia } from '~/types/media';\nimport { getAnimeRecentEpisodes } from '~/services/consumet/anilist/anilist.server';\nimport { authenticate } from '~/services/supabase';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Recent Anime Episodes' },\n  { name: 'description', content: 'Recent Anime Episodes' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/anime/recent-episodes' },\n  { property: 'og:title', content: 'Sora - Recent Anime Episodes' },\n  { property: 'og:description', content: 'Recent Anime Episodes' },\n  { name: 'twitter:title', content: 'Sora - Recent Anime Episodes' },\n  { name: 'twitter:description', content: 'Recent Anime Episodes' },\n]);\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page'));\n  let provider = url.searchParams.get('provider') as 'gogoanime' | 'zoro' | undefined;\n  if (!page && (page < 1 || page > 1000)) page = 1;\n  if (!provider) provider = 'gogoanime';\n\n  return json(\n    {\n      items: await getAnimeRecentEpisodes(provider, page, 20),\n      provider,\n    },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.default },\n    },\n  );\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/anime/recent-episodes\" key=\"anime-recent-episodes\">\n      {t('recent-episodes')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('anime'),\n    subtitle: t('recent-episodes'),\n    showImage: false,\n  }),\n};\n\nconst RecentEpisodes = () => {\n  const { items, provider } = useLoaderData<typeof loader>();\n  const location = useLocation();\n  const navigate = useNavigate();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate('/anime/trending');\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      return;\n    }\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <MediaList\n        currentPage={items?.currentPage || 1}\n        hasNextPage={items?.hasNextPage || false}\n        items={items?.results as IMedia[]}\n        itemsType=\"episode\"\n        listName={t('recent-episodes')}\n        listType=\"grid\"\n        provider={provider}\n      />\n    </motion.div>\n  );\n};\n\nexport default RecentEpisodes;\n"
  },
  {
    "path": "app/routes/anime+/trending.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\n\nimport type { Handle } from '~/types/handle';\nimport type { IMedia } from '~/types/media';\nimport { getAnimeTrending } from '~/services/consumet/anilist/anilist.server';\nimport { authenticate } from '~/services/supabase';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Trending Anime' },\n  { name: 'description', content: 'Trending Anime' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/anime/trending' },\n  { property: 'og:title', content: 'Sora - Trending Anime' },\n  { property: 'og:description', content: 'Trending Anime' },\n  { name: 'twitter:title', content: 'Sora - Trending Anime' },\n  { name: 'twitter:description', content: 'Trending Anime' },\n]);\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page'));\n  if (!page && (page < 1 || page > 1000)) page = 1;\n\n  return json(\n    {\n      items: await getAnimeTrending(page, 20),\n    },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.trending },\n    },\n  );\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/anime/trending\" key=\"anime-trending\">\n      {t('trending-anime')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('anime'),\n    subtitle: t('trending.title'),\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst TrendingAnime = () => {\n  const { items } = useLoaderData<typeof loader>();\n  const location = useLocation();\n  const navigate = useNavigate();\n  const isHydrated = useHydrated();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate('/anime/popular');\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate('/anime/recent-episodes');\n    }\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <MediaList\n        currentPage={items?.currentPage || 1}\n        hasNextPage={items?.hasNextPage || false}\n        items={items?.results as IMedia[]}\n        itemsType=\"anime\"\n        listName=\"Trending Anime\"\n        listType=\"grid\"\n        showListTypeChangeButton\n      />\n    </motion.div>\n  );\n};\n\nexport default TrendingAnime;\n"
  },
  {
    "path": "app/routes/anime.tsx",
    "content": "import { Outlet } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport { animePages } from '~/constants/tabLinks';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  {\n    name: 'keywords',\n    content:\n      'watch free anime, free anime to watch online, watch anime online free, free anime streaming, free anime full, free anime download, watch anime hd, anime to watch, hd anime, stream anime, anime to stream, watch anime free',\n  },\n  { property: 'og:image', content: 'https://sorachill.vercel.app/api/ogimage?it=anime' },\n  { name: 'twitter:image', content: 'https://sorachill.vercel.app/api/ogimage?it=anime' },\n]);\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/anime\" key=\"anime-index\">\n      {t('anime')}\n    </BreadcrumbItem>\n  ),\n  showTabLink: true,\n  tabLinkPages: animePages,\n  tabLinkTo: () => '/anime',\n  hideTabLinkWithLocation: (locationPathname: string) => {\n    if (locationPathname.split('/')[2]?.match(/^\\d+$/) || locationPathname === '/anime')\n      return true;\n    return false;\n  },\n};\n\nconst AnimeIndexPage = () => <Outlet />;\n\nexport default AnimeIndexPage;\n"
  },
  {
    "path": "app/routes/api+/color-palette.ts",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\n\nimport { authenticate } from '~/services/supabase';\nimport { cachified, lruCache } from '~/utils/server/cache.server';\n\ninterface Result {\n  color: ColorPalette;\n}\n\nexport interface ColorPalette {\n  '50': string;\n  '100': string;\n  '200': string;\n  '300': string;\n  '400': string;\n  '500': string;\n  '600': string;\n  '700': string;\n  '800': string;\n  '900': string;\n}\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n  const url = new URL(request.url);\n  const color = url.searchParams.get('color');\n  const colorData = await cachified({\n    key: `color-${color}`,\n    ttl: 1000 * 60 * 60 * 24 * 30,\n    staleWhileRevalidate: 1000 * 60 * 60 * 24 * 365,\n    cache: lruCache,\n    request,\n    getFreshValue: async () => {\n      try {\n        const res = await fetch(`https://www.tints.dev/api/color/${color}`);\n        if (!res.ok) throw new Error(JSON.stringify(await res.json()));\n        const data = (await res.json()) as Result;\n        return data;\n      } catch (error) {\n        // eslint-disable-next-line no-console\n        console.error(error);\n        return { error: 'Something went wrong' };\n      }\n    },\n    checkValue: (value: unknown) => {\n      if (typeof value === 'object' && value !== null) {\n        return Object.keys(value).length > 0;\n      }\n      return false;\n    },\n  });\n  if ((colorData as { error: string })?.error) {\n    return json(colorData, { status: 500 });\n  }\n  return json(colorData, { status: 200, headers: { 'Cache-Control': 'max-age=31536000' } });\n};\n"
  },
  {
    "path": "app/routes/api+/history.ts",
    "content": "import { redirect, type ActionFunctionArgs } from '@remix-run/node';\n\nimport { authenticate, insertHistory } from '~/services/supabase';\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n  const headers = new Headers();\n  const user = await authenticate(request, true, true);\n\n  if (!user) return new Response(null, { headers, status: 401 });\n\n  const data = await request.clone().formData();\n\n  const user_id = data.get('user_id')?.toString();\n  const duration = Number(data.get('duration'));\n  const watched = Number(data.get('watched'));\n  const route = data.get('route')?.toString();\n  const poster = data.get('poster')?.toString();\n  const title = data.get('title')?.toString();\n  const overview = data.get('overview')?.toString();\n  const season = data.get('season')?.toString();\n  const episode = data.get('episode')?.toString();\n  const media_type = data.get('media_type')?.toString() as 'movie' | 'tv' | 'anime';\n\n  if (user.id !== user_id) return new Response(null, { headers, status: 400 });\n\n  if (user_id && duration && route) {\n    const { error } = await insertHistory({\n      user_id: user_id,\n      media_type,\n      duration: Math.round(duration),\n      watched: Math.floor(watched),\n      media_id: route.split('/')[2],\n      route: route,\n      poster: poster,\n      title: title,\n      overview: overview,\n      season: season,\n      episode: episode,\n    });\n\n    if (!error) return new Response(null, { status: 201 });\n  }\n\n  return new Response(null, { status: 400 });\n};\n\nexport const loader = async () => {\n  return redirect('/');\n};\n"
  },
  {
    "path": "app/routes/api+/image.ts",
    "content": "import os from 'os';\nimport path from 'path';\nimport BaseCache from '@next-boost/hybrid-disk-cache';\nimport type { LoaderFunction } from '@remix-run/node';\nimport { sharpTransformer } from 'remix-image-sharp';\nimport {\n  Cache,\n  CacheStatus,\n  fetchResolver,\n  fsResolver,\n  imageLoader,\n  type CacheConfig,\n  type Resolver,\n} from 'remix-image/server';\n\nimport { authenticate } from '~/services/supabase';\n\nexport const fetchImage: Resolver = async (asset, url, options, basePath) => {\n  if (url.startsWith('/') && (url.length === 1 || url[1] !== '/')) {\n    return fsResolver(asset, url, options, basePath);\n  } else {\n    return fetchResolver(asset, url, options, basePath);\n  }\n};\n\nexport interface DiskCacheConfig extends CacheConfig {\n  /**\n   * Path: the relative path where the cache should be stored\n   */\n  path: string;\n}\n\nexport class CustomDiskCache extends Cache {\n  config: DiskCacheConfig;\n  cache: BaseCache;\n\n  constructor(config: Partial<DiskCacheConfig> | null | undefined = {}) {\n    super();\n\n    this.config = {\n      path: config?.path ?? 'tmp/img',\n      ttl: config?.ttl ?? 24 * 60 * 60 * 30,\n      tbd: config?.tbd ?? 365 * 24 * 60 * 60,\n    };\n\n    this.cache = new BaseCache(this.config);\n  }\n\n  async status(key: string): Promise<CacheStatus> {\n    return (await this.cache.has(key)) as CacheStatus;\n  }\n\n  async has(key: string): Promise<boolean> {\n    return (await this.status(key)) !== CacheStatus.MISS;\n  }\n\n  async get(key: string): Promise<Uint8Array | null> {\n    if (!(await this.has(key))) {\n      return null;\n    }\n\n    const cacheValue = (await this.cache.get(key))!;\n\n    await this.cache.set(key, cacheValue);\n\n    return cacheValue;\n  }\n\n  async set(key: string, resultImg: Uint8Array): Promise<void> {\n    await this.cache.set(key, resultImg as Buffer);\n  }\n\n  async clear(): Promise<void> {\n    await this.cache.purge();\n  }\n}\n\nconst vercelUrl = process.env.VERCEL_URL || '';\nconst fixedVercelUrl = vercelUrl.startsWith('https') ? vercelUrl : `https://${vercelUrl}`;\n\nconst config = {\n  selfUrl: process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : fixedVercelUrl,\n  cache: new CustomDiskCache({\n    path: path.join(os.tmpdir(), 'img'),\n  }),\n  redirectOnFail: true,\n  resolver: fetchImage,\n  transformer: sharpTransformer,\n  basePath: process.env.NODE_ENV === 'development' ? 'public' : '/',\n  verbose: false,\n};\n\nexport const loader: LoaderFunction = async ({ request }) => {\n  await authenticate(request, undefined, true);\n  return imageLoader(config, request);\n};\n"
  },
  {
    "path": "app/routes/api+/media.ts",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\n\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getImages, getVideos } from '~/services/tmdb/tmdb.server';\nimport { CACHE_CONTROL } from '~/utils/server/http';\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const url = new URL(request.url);\n  const id = Number(url.searchParams.get('id'));\n  if (!id) throw new Response('Not Found', { status: 404 });\n\n  const type = url.searchParams.get('type') as 'movie' | 'tv';\n  if (!type) throw new Response('Not Found', { status: 404 });\n\n  const isFetchVideos = url.searchParams.get('video') === 'true';\n  const isFetchImages = url.searchParams.get('image') === 'true';\n\n  const [images, videos] = await Promise.all([\n    isFetchImages ? getImages(type, id, locale) : undefined,\n    isFetchVideos ? getVideos(type, id) : undefined,\n  ]);\n\n  return json(\n    {\n      ...(images && { images }),\n      ...(videos && { videos }),\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.default,\n      },\n    },\n  );\n};\n"
  },
  {
    "path": "app/routes/api+/ogimage.tsx",
    "content": "import { json, type HeadersFunction, type LoaderFunctionArgs } from '@remix-run/node';\n\nimport { getMovieDetail, getTvShowDetail } from '~/services/tmdb/tmdb.server';\nimport type { IMovieDetail, ITvShowDetail } from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport { generateMovieSvg, generatePng, generateSvg } from '~/utils/server/og.server';\n\nexport const headers: HeadersFunction = () => {\n  return { 'Cache-Control': 'public, max-age=31536000, immutable' };\n};\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n  const url = new URL(request.url);\n\n  const mid = url.searchParams.get('m') ?? null;\n  const mType = url.searchParams.get('mt') ?? null;\n  const imageType = url.searchParams.get('it') ?? null;\n\n  if (mid && mType && !imageType) {\n    const movieDetail =\n      mType === 'movie' ? await getMovieDetail(Number(mid)) : await getTvShowDetail(Number(mid));\n    const title =\n      (movieDetail as IMovieDetail)?.title || (movieDetail as ITvShowDetail)?.name || '';\n    const posterPath = movieDetail?.poster_path\n      ? TMDB?.posterUrl(movieDetail?.poster_path || '', 'w342')\n      : undefined;\n    const backdropPath = movieDetail?.backdrop_path\n      ? TMDB?.backdropUrl(movieDetail?.backdrop_path || '', 'w1280')\n      : undefined;\n    const releaseYear = new Date(\n      (movieDetail as IMovieDetail)?.release_date ||\n        (movieDetail as ITvShowDetail)?.first_air_date ||\n        '',\n    ).getFullYear();\n    const svg = await generateMovieSvg({\n      title,\n      cover: backdropPath,\n      poster: posterPath,\n      voteAverage: movieDetail?.vote_average?.toFixed(1),\n      genres: movieDetail?.genres?.splice(0, 4),\n      releaseYear,\n      numberOfEpisodes: (movieDetail as ITvShowDetail)?.number_of_episodes,\n      numberOfSeasons: (movieDetail as ITvShowDetail)?.number_of_seasons,\n      runtime: (movieDetail as IMovieDetail)?.runtime,\n      productionCompany:\n        (movieDetail as IMovieDetail)?.production_companies![0]?.name ||\n        (movieDetail as ITvShowDetail)?.production_companies![0]?.name,\n    });\n    return generatePng(svg);\n  }\n  if (!mid && !mType && imageType) {\n    let title;\n    let cover;\n    switch (imageType) {\n      case 'home':\n        title = 'SORA';\n        cover =\n          'https://raw.githubusercontent.com/Khanhtran47/Sora/master/app/assets/images/background-default.jpg';\n        break;\n      case 'movies':\n        title = 'SORA Movies';\n        cover =\n          'https://image.tmdb.org/t/p/w1280_filter(duotone,190235,ad47dd)/lXhgCODAbBXL5buk9yEmTpOoOgR.jpg';\n        break;\n      case 'tvshows':\n        title = 'SORA TV Shows';\n        cover =\n          'https://image.tmdb.org/t/p/w1280_filter(duotone,00192f,00baff)/etj8E2o0Bud0HkONVQPjyCkIvpv.jpg';\n        break;\n      case 'people':\n        title = 'SORA People';\n        cover =\n          'https://image.tmdb.org/t/p/w1280_filter(duotone,190235,ad47dd)/uDgy6hyPd82kOHh6I95FLtLnj6p.jpg';\n        break;\n      case 'anime':\n        title = 'SORA Anime';\n        cover =\n          'https://image.tmdb.org/t/p/w1280_filter(duotone,00192f,00baff)/rqbCbjB19amtOtFQbb3K2lgm2zv.jpg';\n        break;\n      case 'search':\n        title = 'SORA Search';\n        cover =\n          'https://image.tmdb.org/t/p/w1280_filter(duotone,00192f,00baff)/Vq4L8A88fNQxBqM27xHtDi4DrL.jpg';\n        break;\n      default:\n        title = 'SORA';\n        cover =\n          'https://raw.githubusercontent.com/Khanhtran47/Sora/master/app/assets/images/background-default.jpg';\n        break;\n    }\n\n    const svg = await generateSvg({\n      title,\n      cover,\n    });\n    return generatePng(svg);\n  }\n  return json({ message: 'Invalid request' }, { status: 400 });\n}\n"
  },
  {
    "path": "app/routes/api+/provider.ts",
    "content": "/* eslint-disable @typescript-eslint/no-throw-literal */\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\n\nimport getProviderList from '~/services/provider.server';\nimport { authenticate } from '~/services/supabase';\nimport { CACHE_CONTROL } from '~/utils/server/http';\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const url = new URL(request.url);\n  const type = url.searchParams.get('type');\n  const title = url.searchParams.get('title');\n  const orgTitle = url.searchParams.get('orgTitle');\n  const year = url.searchParams.get('year');\n  const season = url.searchParams.get('season');\n  const aid = url.searchParams.get('aid');\n  const animeType = url.searchParams.get('animeType');\n  const isEnded = url.searchParams.get('isEnded');\n  const tmdbId = url.searchParams.get('tmdbId');\n  if (!title || !type) throw new Response('Missing params', { status: 400 });\n  const provider = await getProviderList({\n    type,\n    title,\n    orgTitle,\n    year,\n    season,\n    animeId: Number(aid),\n    animeType,\n    isEnded: isEnded === 'true',\n    tmdbId: Number(tmdbId),\n  });\n  if (provider && provider.length > 0)\n    return json({ provider }, { status: 200, headers: { 'Cache-Control': CACHE_CONTROL.default } });\n\n  return json(\n    {\n      provider: undefined,\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.default,\n      },\n    },\n  );\n};\n"
  },
  {
    "path": "app/routes/api+/subtitles.download.ts",
    "content": "/* eslint-disable @typescript-eslint/no-throw-literal */\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\n\nimport { getSubtitleDownload } from '~/services/open-subtitles/open-subtitles.server';\nimport { authenticate } from '~/services/supabase';\nimport { CACHE_CONTROL } from '~/utils/server/http';\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n  const url = new URL(request.url);\n  const file_id = url.searchParams.get('file_id');\n  const sub_format = url.searchParams.get('sub_format') as 'srt' | 'webvtt';\n  if (!file_id || !sub_format) throw { status: 400, message: 'Missing file_id or sub_format' };\n  const subtitle = await getSubtitleDownload(Number(file_id), sub_format);\n\n  return json(\n    { subtitle },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.default,\n      },\n    },\n  );\n};\n"
  },
  {
    "path": "app/routes/api+/subtitles.search.ts",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\n\nimport { getSubtitlesSearch } from '~/services/open-subtitles/open-subtitles.server';\nimport { authenticate } from '~/services/supabase';\nimport { CACHE_CONTROL } from '~/utils/server/http';\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n  const url = new URL(request.url);\n  const tmdb_id = url.searchParams.get('tmdb_id');\n  const parent_tmdb_id = url.searchParams.get('parent_tmdb_id');\n  const season_number = url.searchParams.get('season_number');\n  const episode_number = url.searchParams.get('episode_number');\n  const query = url.searchParams.get('query');\n  const language = url.searchParams.get('language');\n  let page = Number(url.searchParams.get('page'));\n  if (!page && (page < 1 || page > 1000)) page = 1;\n  const subtitlesSearch = await getSubtitlesSearch(\n    undefined,\n    undefined,\n    tmdb_id ? Number(tmdb_id) : undefined,\n    undefined,\n    undefined,\n    parent_tmdb_id ? Number(parent_tmdb_id) : undefined,\n    query ? query : undefined,\n    undefined,\n    episode_number ? Number(episode_number) : undefined,\n    undefined,\n    undefined,\n    language ? language : undefined,\n    undefined,\n    undefined,\n    undefined,\n    undefined,\n    undefined,\n    page,\n    season_number ? Number(season_number) : undefined,\n    undefined,\n    undefined,\n    undefined,\n    undefined,\n  );\n\n  return json(\n    { subtitlesSearch },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.default,\n      },\n    },\n  );\n};\n"
  },
  {
    "path": "app/routes/api+/youtube-video.ts",
    "content": "/* eslint-disable @typescript-eslint/no-throw-literal */\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\n\nimport { authenticate } from '~/services/supabase';\nimport { getYoutubeVideo } from '~/services/youtube/youtube.server';\nimport { CACHE_CONTROL } from '~/utils/server/http';\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n  const url = new URL(request.url);\n  const id = url.searchParams.get('id');\n  if (!id) throw new Response('No id', { status: 400 });\n  const youtubeVideo = await getYoutubeVideo(id, true, true);\n  if (!youtubeVideo) throw new Response('Not Found', { status: 404 });\n\n  return json(\n    { youtubeVideo },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.default,\n      },\n    },\n  );\n};\n"
  },
  {
    "path": "app/routes/design-system+/accordion.tsx",
    "content": "import { Accordion, AccordionItem } from '@nextui-org/accordion';\nimport { Link } from '@nextui-org/link';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/accordion\" key=\"design-accordion\">\n      Accordion\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Accordion',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst AccordionPage = () => {\n  const defaultContent =\n    'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.';\n  return (\n    <>\n      <h2>Accordion</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://nextui-docs-v2.vercel.app/docs/components/accordion#api\"\n      >\n        API Reference\n      </Link>\n      <Accordion>\n        <AccordionItem key=\"1\" aria-label=\"Accordion 1\" title=\"Accordion 1\">\n          {defaultContent}\n        </AccordionItem>\n        <AccordionItem key=\"2\" aria-label=\"Accordion 2\" title=\"Accordion 2\">\n          {defaultContent}\n        </AccordionItem>\n        <AccordionItem key=\"3\" aria-label=\"Accordion 3\" title=\"Accordion 3\">\n          {defaultContent}\n        </AccordionItem>\n      </Accordion>\n    </>\n  );\n};\n\nexport default AccordionPage;\n"
  },
  {
    "path": "app/routes/design-system+/avatar.tsx",
    "content": "import { Avatar } from '@nextui-org/avatar';\nimport { Link } from '@nextui-org/link';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/avatar\" key=\"design-avatar\">\n      Avatar\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Avatar',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst AvatarPage = () => {\n  return (\n    <>\n      <h2>Avatar</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://nextui-docs-v2.vercel.app/docs/components/avatar#api\"\n      >\n        API Reference\n      </Link>\n      <div className=\"flex items-center gap-3\">\n        <Avatar src=\"https://i.pravatar.cc/150?u=a042581f4e29026024d\" />\n        <Avatar name=\"Junior\" />\n        <Avatar src=\"https://i.pravatar.cc/150?u=a042581f4e29026704d\" />\n        <Avatar name=\"Jane\" />\n        <Avatar src=\"https://i.pravatar.cc/150?u=a04258114e29026702d\" />\n        <Avatar name=\"Joe\" />\n      </div>\n    </>\n  );\n};\n\nexport default AvatarPage;\n"
  },
  {
    "path": "app/routes/design-system+/badge.tsx",
    "content": "import { Avatar } from '@nextui-org/avatar';\nimport { Badge } from '@nextui-org/badge';\nimport { Link } from '@nextui-org/link';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/badge\" key=\"design-badge\">\n      Badge\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Badge',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst BadgePage = () => {\n  return (\n    <>\n      <h2>Badge</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://nextui-docs-v2.vercel.app/docs/components/badge#api\"\n      >\n        API Reference\n      </Link>\n      <Badge content=\"5\" color=\"primary\">\n        <Avatar radius=\"lg\" src=\"https://i.pravatar.cc/300?u=a042581f4e29026709d\" />\n      </Badge>\n    </>\n  );\n};\n\nexport default BadgePage;\n"
  },
  {
    "path": "app/routes/design-system+/button.tsx",
    "content": "import { useState } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { Link } from '@nextui-org/link';\nimport { Switch } from '@nextui-org/switch';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '~/components/elements/Select';\nimport Search from '~/assets/icons/SearchIcon';\nimport Settings from '~/assets/icons/SettingsIcon';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/button\" key=\"design-button\">\n      Button\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Button',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst ButtonPage = () => {\n  const [size, setSize] = useState('md');\n  const [radius, setRadius] = useState('xl');\n  const [isFullWidth, setIsFullWidth] = useState(true);\n  return (\n    <>\n      <h2>Button</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://nextui-docs-v2.vercel.app/docs/components/button#api\"\n      >\n        API Reference\n      </Link>\n      <p className=\"text-base tracking-wide md:text-lg\">Default</p>\n      <Button>Default</Button>\n      <p className=\"text-base tracking-wide md:text-lg\">Disabled</p>\n      <Button isDisabled>Disabled</Button>\n      <p className=\"text-base tracking-wide md:text-lg\">Sizes</p>\n      <Select defaultValue=\"md\" value={size} onValueChange={(value) => setSize(value)}>\n        <SelectTrigger className=\"w-[80px]\">\n          <SelectValue placeholder=\"Button size\" />\n        </SelectTrigger>\n        <SelectContent>\n          <SelectItem value=\"xs\">xs</SelectItem>\n          <SelectItem value=\"sm\">sm</SelectItem>\n          <SelectItem value=\"md\">md</SelectItem>\n          <SelectItem value=\"lg\">lg</SelectItem>\n          <SelectItem value=\"xl\">xl</SelectItem>\n        </SelectContent>\n      </Select>\n      <Button size={size as 'sm' | 'md' | 'lg' | undefined}>{size}</Button>\n      <p className=\"text-base tracking-wide md:text-lg\">Radius</p>\n      <Select defaultValue=\"xl\" value={radius} onValueChange={(value) => setRadius(value)}>\n        <SelectTrigger className=\"w-[80px]\">\n          <SelectValue placeholder=\"Button size\" />\n        </SelectTrigger>\n        <SelectContent>\n          <SelectItem value=\"none\">none</SelectItem>\n          <SelectItem value=\"base\">base</SelectItem>\n          <SelectItem value=\"sm\">sm</SelectItem>\n          <SelectItem value=\"md\">md</SelectItem>\n          <SelectItem value=\"lg\">lg</SelectItem>\n          <SelectItem value=\"xl\">xl</SelectItem>\n          <SelectItem value=\"2xl\">2xl</SelectItem>\n          <SelectItem value=\"3xl\">3xl</SelectItem>\n          <SelectItem value=\"full\">full</SelectItem>\n        </SelectContent>\n      </Select>\n      <Button radius={radius as 'md' | 'sm' | 'lg' | 'none' | 'full' | undefined}>{radius}</Button>\n      <p className=\"text-base tracking-wide md:text-lg\">Icons</p>\n      <Button startContent={<Search />} endContent={<Settings />}>\n        Button\n      </Button>\n      <Button isIconOnly>\n        <Settings />\n      </Button>\n      <p className=\"text-base tracking-wide md:text-lg\">Loading</p>\n      <Button isDisabled isLoading>\n        Loading\n      </Button>\n      <p className=\"text-base tracking-wide md:text-lg\">Disable Ripple Animation</p>\n      <Button disableRipple>Button</Button>\n      <p className=\"text-base tracking-wide md:text-lg\">Full Width</p>\n      <Switch isSelected={isFullWidth} onValueChange={(value) => setIsFullWidth(value)} />\n      <Button fullWidth={isFullWidth}>Button</Button>\n      <p className=\"text-base tracking-wide md:text-lg\">Colors</p>\n      <div className=\"flex flex-row flex-wrap items-center justify-start gap-4\">\n        <Button>Default</Button>\n        <Button color=\"primary\">Primary</Button>\n        <Button color=\"secondary\">Secondary</Button>\n        <Button color=\"success\">Success</Button>\n        <Button color=\"warning\">Warning</Button>\n        <Button color=\"danger\">Danger</Button>\n        <Button className=\"bg-gradient-to-tr from-primary to-secondary\">Custom colors</Button>\n      </div>\n      <p className=\"text-base tracking-wide md:text-lg\">Variants</p>\n      <div className=\"flex flex-row flex-wrap items-center justify-start gap-4\">\n        <Button variant=\"solid\">Solid</Button>\n        <Button variant=\"solid\" color=\"primary\">\n          Solid\n        </Button>\n        <Button variant=\"solid\" color=\"secondary\">\n          Solid\n        </Button>\n        <Button variant=\"solid\" color=\"success\">\n          Solid\n        </Button>\n        <Button variant=\"solid\" color=\"warning\">\n          Solid\n        </Button>\n        <Button variant=\"solid\" color=\"danger\">\n          Solid\n        </Button>\n      </div>\n      <div className=\"flex flex-row flex-wrap items-center justify-start gap-4\">\n        <Button variant=\"bordered\">Bordered</Button>\n        <Button variant=\"bordered\" color=\"primary\">\n          Bordered\n        </Button>\n        <Button variant=\"bordered\" color=\"secondary\">\n          Bordered\n        </Button>\n        <Button variant=\"bordered\" color=\"success\">\n          Bordered\n        </Button>\n        <Button variant=\"bordered\" color=\"warning\">\n          Bordered\n        </Button>\n        <Button variant=\"bordered\" color=\"danger\">\n          Bordered\n        </Button>\n      </div>\n      <div className=\"flex flex-row flex-wrap items-center justify-start gap-4\">\n        <Button variant=\"light\">Light</Button>\n        <Button variant=\"light\" color=\"primary\">\n          Light\n        </Button>\n        <Button variant=\"light\" color=\"secondary\">\n          Light\n        </Button>\n        <Button variant=\"light\" color=\"success\">\n          Light\n        </Button>\n        <Button variant=\"light\" color=\"warning\">\n          Light\n        </Button>\n        <Button variant=\"light\" color=\"danger\">\n          Light\n        </Button>\n      </div>\n      <div className=\"flex flex-row flex-wrap items-center justify-start gap-4\">\n        <Button variant=\"flat\">flat</Button>\n        <Button variant=\"flat\" color=\"primary\">\n          flat\n        </Button>\n        <Button variant=\"flat\" color=\"secondary\">\n          flat\n        </Button>\n        <Button variant=\"flat\" color=\"success\">\n          flat\n        </Button>\n        <Button variant=\"flat\" color=\"warning\">\n          flat\n        </Button>\n        <Button variant=\"flat\" color=\"danger\">\n          flat\n        </Button>\n      </div>\n      <div className=\"flex flex-row flex-wrap items-center justify-start gap-4\">\n        <Button variant=\"faded\">faded</Button>\n        <Button variant=\"faded\" color=\"primary\">\n          faded\n        </Button>\n        <Button variant=\"faded\" color=\"secondary\">\n          faded\n        </Button>\n        <Button variant=\"faded\" color=\"success\">\n          faded\n        </Button>\n        <Button variant=\"faded\" color=\"warning\">\n          faded\n        </Button>\n        <Button variant=\"faded\" color=\"danger\">\n          faded\n        </Button>\n      </div>\n      <div className=\"flex flex-row flex-wrap items-center justify-start gap-4\">\n        <Button variant=\"shadow\">shadow</Button>\n        <Button variant=\"shadow\" color=\"primary\">\n          shadow\n        </Button>\n        <Button variant=\"shadow\" color=\"secondary\">\n          shadow\n        </Button>\n        <Button variant=\"shadow\" color=\"success\">\n          shadow\n        </Button>\n        <Button variant=\"shadow\" color=\"warning\">\n          shadow\n        </Button>\n        <Button variant=\"shadow\" color=\"danger\">\n          shadow\n        </Button>\n      </div>\n      <div className=\"flex flex-row flex-wrap items-center justify-start gap-4\">\n        <Button variant=\"ghost\">ghost</Button>\n        <Button variant=\"ghost\" color=\"primary\">\n          ghost\n        </Button>\n        <Button variant=\"ghost\" color=\"secondary\">\n          ghost\n        </Button>\n        <Button variant=\"ghost\" color=\"success\">\n          ghost\n        </Button>\n        <Button variant=\"ghost\" color=\"warning\">\n          ghost\n        </Button>\n        <Button variant=\"ghost\" color=\"danger\">\n          ghost\n        </Button>\n      </div>\n    </>\n  );\n};\n\nexport default ButtonPage;\n"
  },
  {
    "path": "app/routes/design-system+/card.tsx",
    "content": "import { Button } from '@nextui-org/button';\nimport { Card, CardBody, CardFooter, CardHeader } from '@nextui-org/card';\nimport { Link } from '@nextui-org/link';\nimport { MimeType } from 'remix-image';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport Image from '~/components/elements/Image';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/card\" key=\"design-card\">\n      Card\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Card',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst CardPage = () => {\n  return (\n    <>\n      <h2>Card</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://nextui-docs-v2.vercel.app/docs/components/card#api\"\n      >\n        API Reference\n      </Link>\n      <p className=\"text-base tracking-wide md:text-lg\">Default</p>\n      <Card className=\"max-w-md\">\n        <CardBody>\n          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed</p>\n        </CardBody>\n      </Card>\n      <p className=\"text-base tracking-wide md:text-lg\">With Divider</p>\n      <Card className=\"max-w-md\">\n        <CardHeader className=\"border-b border-divider\">\n          <strong>Description</strong>\n        </CardHeader>\n        <CardBody className=\"py-8\">\n          <p>The Object constructor creates an object wrapper for the given value.</p>\n        </CardBody>\n        <CardFooter className=\"border-t border-divider\">\n          <p>\n            When called in a non-constructor context, Object behaves identically to{' '}\n            {/* <Code color=\"primary\">new Object()</Code>. */}\n          </p>\n        </CardFooter>\n      </Card>\n      <p className=\"text-base tracking-wide md:text-lg\">With Footer</p>\n      <Card className=\"max-w-md p-4\">\n        <CardHeader className=\"flex gap-3\">\n          {' '}\n          <Image\n            alt=\"nextui logo\"\n            height={34}\n            radius=\"lg\"\n            src=\"https://avatars.githubusercontent.com/u/86160567?s=200&v=4\"\n            width={34}\n          />\n          <div className=\"flex flex-col\">\n            <b className=\"text-lg\">NextUI</b>\n            <p className=\"text-default-500\">nextui.org</p>\n          </div>\n        </CardHeader>\n        <CardBody className=\"py-2\">\n          <p>Make beautiful websites regardless of your design experience.</p>\n        </CardBody>\n        <CardFooter>\n          <Link isExternal showAnchorIcon href=\"https://github.com/nextui-org/nextui\">\n            Visit source code on GitHub.\n          </Link>\n        </CardFooter>\n      </Card>\n      <p className=\"text-base tracking-wide md:text-lg\">With Abs Image Header</p>\n      <Card className=\"max-w-[330px]\">\n        <CardHeader className=\"absolute top-2 z-20\">\n          <div className=\"flex flex-col\">\n            <p className=\"text-xs font-bold uppercase text-white/60\">What to watch</p>\n            <p className=\"text-2xl text-white\">Stream the Apple event</p>\n          </div>\n        </CardHeader>\n        <Image\n          alt=\"Card background\"\n          classNames={{\n            img: 'h-[440px] w-full object-cover',\n          }}\n          height={440}\n          src={'https://storiesv2.nextui.org/static/media/apple-event.64d9fae8.jpeg'}\n          width={330}\n        />\n      </Card>\n      <p className=\"text-base tracking-wide md:text-lg\">With Abs Image Header Footer</p>\n      <Card className=\"w-[330px] bg-zinc-100 dark:bg-zinc-100\">\n        <CardHeader className=\"absolute top-2 z-10\">\n          <div className=\"flex flex-col gap-2\">\n            <p className=\"text-xs font-bold uppercase text-black/40\">New</p>\n            <h4 className=\"text-3xl font-medium text-black\">HomePod mini</h4>\n            <p className=\"pr-1.5 text-sm text-black/80\">\n              Room-filling sound, Intelligent assistant. Smart home control. Works seamlessly with\n              iPhone. Check it out\n            </p>\n          </div>\n        </CardHeader>\n        <Image\n          alt=\"Card background\"\n          classNames={{\n            img: 'h-[440px] w-full object-contain pt-10',\n          }}\n          height={440}\n          src={'https://storiesv2.nextui.org/static/media/homepod.1229711c.jpeg'}\n          width={300}\n        />\n        <CardFooter className=\"absolute bottom-0 z-10 justify-between\">\n          <div>\n            <p className=\"text-xs text-black/80\">Available soon.</p>\n            <p className=\"text-xs text-black/80\">Get notified.</p>\n          </div>\n          <Button radius=\"full\">Notify Me</Button>\n        </CardFooter>\n      </Card>\n      <p className=\"text-base tracking-wide md:text-lg\">Cover Image</p>\n      <Card isFooterBlurred className=\"h-[400px] w-[330px] sm:col-span-5\">\n        <CardHeader className=\"absolute top-1 z-10 flex-col items-start\">\n          <p className=\"text-xs font-bold uppercase text-white/60\">New</p>\n          <h4 className=\"text-2xl font-medium text-black\">Acme camera</h4>\n        </CardHeader>\n        <img\n          alt=\"Card example background\"\n          className=\"h-full w-full -translate-y-10 scale-125 object-cover\"\n          src=\"https://nextui.org/images/card-example-6.jpeg\"\n        />\n        <CardFooter className=\"absolute bottom-0 z-10 justify-between border-t border-slate-300 bg-white/30\">\n          <div>\n            <p className=\"text-xs text-black\">Available soon.</p>\n            <p className=\"text-xs text-black\">Get notified.</p>\n          </div>\n          <Button color=\"secondary\" radius=\"full\" size=\"sm\" variant=\"flat\">\n            Notify Me\n          </Button>\n        </CardFooter>\n      </Card>\n      <p className=\"text-base tracking-wide md:text-lg\">Center Image</p>\n      <Card className=\"max-w-fit px-0 py-4\">\n        <CardHeader className=\"flex-col !items-start px-4 pb-0 pt-2\">\n          <p className=\"text-xs font-bold uppercase\">Daily Mix</p>\n          <small className=\"text-default-500\">12 Tracks</small>\n          <h4 className=\"text-lg font-bold\">Frontend Radio</h4>\n        </CardHeader>\n        <CardBody className=\"overflow-visible py-2\">\n          <Image\n            disableSkeleton={false}\n            alt=\"Card background\"\n            src={'https://storiesv2.nextui.org/static/media/local-image-1.35051865.jpeg'}\n            width={300}\n            loading=\"lazy\"\n            placeholder=\"empty\"\n            options={{ contentType: MimeType.WEBP }}\n            responsive={[{ size: { width: 300, height: 180 } }]}\n          />\n        </CardBody>\n      </Card>\n      <p className=\"text-base tracking-wide md:text-lg\">With Abs Custom Size Image Header</p>\n      <Card className=\"max-w-[330px]\">\n        <CardHeader className=\"absolute top-2 z-20\">\n          <div className=\"flex flex-col\">\n            <p className=\"text-xs font-bold uppercase text-white/60\">What to watch</p>\n            <p className=\"text-2xl text-white\">Stream the Apple event</p>\n          </div>\n        </CardHeader>\n        <Image\n          alt=\"Card background\"\n          className=\"h-[440px] w-full object-cover\"\n          src={'https://storiesv2.nextui.org/static/media/apple-event.64d9fae8.jpeg'}\n          isZoomed\n          disableSkeleton={false}\n          loading=\"lazy\"\n          height={440}\n          width={330}\n          placeholder=\"empty\"\n          options={{ contentType: MimeType.WEBP }}\n          responsive={[{ size: { width: 330, height: 440 } }]}\n        />\n      </Card>\n    </>\n  );\n};\n\nexport default CardPage;\n"
  },
  {
    "path": "app/routes/design-system+/colors.tsx",
    "content": "import type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/colors\" key=\"design-colors\">\n      Colors\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Colors',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst ColorsPage = () => {\n  return (\n    <>\n      <h2 className=\"m-0\">Semantic colors</h2>\n      <div className=\"flex flex-col gap-3\">\n        Layout:\n        <div className=\"mb-4 flex flex-row flex-wrap gap-4\">\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-background text-foreground shadow-lg\">\n            background\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-foreground text-background shadow-lg\">\n            foreground\n          </div>\n          <div className=\"bg-border flex h-32 w-32 items-center justify-center rounded-medium text-content3-foreground shadow-lg\">\n            border\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-focus text-primary-foreground shadow-lg\">\n            focus\n          </div>\n        </div>\n        Content:\n        <div className=\"mb-4 flex flex-row flex-wrap gap-4\">\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-content1 text-content1-foreground shadow-lg\">\n            content1\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-content2 text-content2-foreground shadow-lg\">\n            content2\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-content3 text-content3-foreground shadow-lg\">\n            content3\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-content4 text-content4-foreground shadow-lg\">\n            content4\n          </div>\n        </div>\n        Base:\n        <div className=\"mb-4 flex flex-row flex-wrap gap-4\">\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-default text-default-foreground shadow-lg\">\n            default\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-primary text-primary-foreground shadow-lg\">\n            primary\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-secondary text-secondary-foreground shadow-lg\">\n            secondary\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-success text-success-foreground shadow-lg\">\n            success\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-warning text-warning-foreground shadow-lg\">\n            warning\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-danger text-danger-foreground shadow-lg\">\n            danger\n          </div>\n        </div>\n        Default:\n        <div className=\"mb-4 flex flex-row flex-wrap gap-4\">\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-default-50 text-default-900 shadow-lg\">\n            default-50\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-default-100 text-default-900 shadow-lg\">\n            default-100\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-default-200 text-default-800 shadow-lg\">\n            default-200\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-default-300 text-default-800 shadow-lg\">\n            default-300\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-default-400 text-default-800 shadow-lg\">\n            default-400\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-default-500 text-default-foreground shadow-lg\">\n            default-500\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-default-600 text-default-50 shadow-lg\">\n            default-600\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-default-700 text-default-100 shadow-lg\">\n            default-700\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-default-800 text-default-100 shadow-lg\">\n            default-800\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-default-900 text-default-100 shadow-lg\">\n            default-900\n          </div>\n        </div>\n        Primary:\n        <div className=\"mb-4 flex flex-row flex-wrap gap-4\">\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-primary-50 text-primary-900 shadow-lg\">\n            primary-50\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-primary-100 text-primary-900 shadow-lg\">\n            primary-100\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-primary-200 text-primary-800 shadow-lg\">\n            primary-200\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-primary-300 text-primary-800 shadow-lg\">\n            primary-300\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-primary-400 text-primary-800 shadow-lg\">\n            primary-400\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-primary-500 text-primary-foreground shadow-lg\">\n            primary-500\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-primary-600 text-primary-50 shadow-lg\">\n            primary-600\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-primary-700 text-primary-100 shadow-lg\">\n            primary-700\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-primary-800 text-primary-100 shadow-lg\">\n            primary-800\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-primary-900 text-primary-100 shadow-lg\">\n            primary-900\n          </div>\n        </div>\n        Secondary:\n        <div className=\"mb-4 flex flex-row flex-wrap gap-4\">\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-secondary-50 text-secondary-900 shadow-lg\">\n            secondary-50\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-secondary-100 text-secondary-900 shadow-lg\">\n            secondary-100\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-secondary-200 text-secondary-800 shadow-lg\">\n            secondary-200\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-secondary-300 text-secondary-800 shadow-lg\">\n            secondary-300\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-secondary-400 text-secondary-800 shadow-lg\">\n            secondary-400\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-secondary-500 text-secondary-foreground shadow-lg\">\n            secondary-500\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-secondary-600 text-secondary-50 shadow-lg\">\n            secondary-600\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-secondary-700 text-secondary-100 shadow-lg\">\n            secondary-700\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-secondary-800 text-secondary-100 shadow-lg\">\n            secondary-800\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-secondary-900 text-secondary-100 shadow-lg\">\n            secondary-900\n          </div>\n        </div>\n        Success:\n        <div className=\"mb-4 flex flex-row flex-wrap gap-4\">\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-success-50 text-success-900 shadow-lg\">\n            success-50\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-success-100 text-success-900 shadow-lg\">\n            success-100\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-success-200 text-success-800 shadow-lg\">\n            success-200\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-success-300 text-success-800 shadow-lg\">\n            success-300\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-success-400 text-success-800 shadow-lg\">\n            success-400\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-success-500 text-success-foreground shadow-lg\">\n            success-500\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-success-600 text-success-50 shadow-lg\">\n            success-600\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-success-700 text-success-100 shadow-lg\">\n            success-700\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-success-800 text-success-100 shadow-lg\">\n            success-800\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-success-900 text-success-100 shadow-lg\">\n            success-900\n          </div>\n        </div>\n        Warning:\n        <div className=\"mb-4 flex flex-row flex-wrap gap-4\">\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-warning-50 text-warning-900 shadow-lg\">\n            warning-50\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-warning-100 text-warning-900 shadow-lg\">\n            warning-100\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-warning-200 text-warning-800 shadow-lg\">\n            warning-200\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-warning-300 text-warning-800 shadow-lg\">\n            warning-300\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-warning-400 text-warning-800 shadow-lg\">\n            warning-400\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-warning-500 text-warning-foreground shadow-lg\">\n            warning-500\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-warning-600 text-warning-50 shadow-lg\">\n            warning-600\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-warning-700 text-warning-100 shadow-lg\">\n            warning-700\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-warning-800 text-warning-100 shadow-lg\">\n            warning-800\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-warning-900 text-warning-100 shadow-lg\">\n            warning-900\n          </div>\n        </div>\n        Danger:\n        <div className=\"mb-4 flex flex-row flex-wrap gap-4\">\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-danger-50 text-danger-900 shadow-lg\">\n            danger-50\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-danger-100 text-danger-900 shadow-lg\">\n            danger-100\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-danger-200 text-danger-800 shadow-lg\">\n            danger-200\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-danger-300 text-danger-800 shadow-lg\">\n            danger-300\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-danger-400 text-danger-800 shadow-lg\">\n            danger-400\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-danger-500 text-danger-foreground shadow-lg\">\n            danger-500\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-danger-600 text-danger-50 shadow-lg\">\n            danger-600\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-danger-700 text-danger-100 shadow-lg\">\n            danger-700\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-danger-800 text-danger-100 shadow-lg\">\n            danger-800\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-danger-900 text-danger-100 shadow-lg\">\n            danger-900\n          </div>\n        </div>\n      </div>\n      <h2 className=\"m-0\">Common colors</h2>\n      <div className=\"flex flex-col gap-3\">\n        App colors\n        <div className=\"mb-4 flex flex-row flex-wrap gap-4\">\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-white text-black shadow-lg\">\n            #FFFFFF\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-black text-white shadow-lg\">\n            #000000\n          </div>\n        </div>\n        Blue\n        <div className=\"mb-4 flex flex-row flex-wrap gap-4\">\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-blue-50 text-black shadow-lg\">\n            #E6F1FE\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-blue-100 text-black shadow-lg\">\n            #CCE3FD\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-blue-200 text-black shadow-lg\">\n            #99C7FB\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-blue-300 text-black shadow-lg\">\n            #66AAF9\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-blue-400 text-black shadow-lg\">\n            #338EF7\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-blue-500 text-black shadow-lg\">\n            #0072F5\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-blue-600 text-white shadow-lg\">\n            #005BC4\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-blue-700 text-white shadow-lg\">\n            #004493\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-blue-800 text-white shadow-lg\">\n            #002E62\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-blue-900 text-white shadow-lg\">\n            #001731\n          </div>\n        </div>\n        Purple\n        <div className=\"mb-4 flex flex-row flex-wrap gap-4\">\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-purple-50 text-black shadow-lg\">\n            #F2EAFA\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-purple-100 text-black shadow-lg\">\n            #E4D4F4\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-purple-200 text-black shadow-lg\">\n            #C9A9E9\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-purple-300 text-black shadow-lg\">\n            #AE7EDE\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-purple-400 text-black shadow-lg\">\n            #9353D3\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-purple-500 text-black shadow-lg\">\n            #7828C8\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-purple-600 text-white shadow-lg\">\n            #6020A0\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-purple-700 text-white shadow-lg\">\n            #481878\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-purple-800 text-white shadow-lg\">\n            #301050\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-purple-900 text-white shadow-lg\">\n            #180828\n          </div>\n        </div>\n        Green\n        <div className=\"mb-4 flex flex-row flex-wrap gap-4\">\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-green-50 text-black shadow-lg\">\n            #E8FAF0\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-green-100 text-black shadow-lg\">\n            #D1F4E0\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-green-200 text-black shadow-lg\">\n            #A2E9C1\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-green-300 text-black shadow-lg\">\n            #74DFA2\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-green-400 text-black shadow-lg\">\n            #45D483\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-green-500 text-black shadow-lg\">\n            #17C964\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-green-600 text-white shadow-lg\">\n            #12A150\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-green-700 text-white shadow-lg\">\n            #0E793C\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-green-800 text-white shadow-lg\">\n            #095028\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-green-900 text-white shadow-lg\">\n            #052814\n          </div>\n        </div>\n        Red\n        <div className=\"mb-4 flex flex-row flex-wrap gap-4\">\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-red-50 text-black shadow-lg\">\n            #FEE7EF\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-red-100 text-black shadow-lg\">\n            #FDD0DF\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-red-200 text-black shadow-lg\">\n            #FAA0BF\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-red-300 text-black shadow-lg\">\n            #F871A0\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-red-400 text-black shadow-lg\">\n            #F54180\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-red-500 text-black shadow-lg\">\n            #F31260\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-red-600 text-white shadow-lg\">\n            #C20E4D\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-red-700 text-white shadow-lg\">\n            #920B3A\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-red-800 text-white shadow-lg\">\n            #610726\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-red-900 text-white shadow-lg\">\n            #310413\n          </div>\n        </div>\n        Pink\n        <div className=\"mb-4 flex flex-row flex-wrap gap-4\">\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-pink-50 text-black shadow-lg\">\n            #FFEDFA\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-pink-100 text-black shadow-lg\">\n            #FFDCF5\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-pink-200 text-black shadow-lg\">\n            #FFB8EB\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-pink-300 text-black shadow-lg\">\n            #FF95E1\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-pink-400 text-black shadow-lg\">\n            #FF71D7\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-pink-500 text-black shadow-lg\">\n            #FF4ECD\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-pink-600 text-white shadow-lg\">\n            #CC3EA4\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-pink-700 text-white shadow-lg\">\n            #992F7B\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-pink-800 text-white shadow-lg\">\n            #661F52\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-pink-900 text-white shadow-lg\">\n            #331029\n          </div>\n        </div>\n        Yellow\n        <div className=\"mb-4 flex flex-row flex-wrap gap-4\">\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-yellow-50 text-black shadow-lg\">\n            #FEF6E9\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-yellow-100 text-black shadow-lg\">\n            #FDEDD3\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-yellow-200 text-black shadow-lg\">\n            #FBDBA7\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-yellow-300 text-black shadow-lg\">\n            #F9C97C\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-yellow-400 text-black shadow-lg\">\n            #F7B750\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-yellow-500 text-black shadow-lg\">\n            #F5A524\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-yellow-600 text-white shadow-lg\">\n            #C4841D\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-yellow-700 text-white shadow-lg\">\n            #936316\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-yellow-800 text-white shadow-lg\">\n            #62420E\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-yellow-900 text-white shadow-lg\">\n            #312107\n          </div>\n        </div>\n        Cyan\n        <div className=\"mb-4 flex flex-row flex-wrap gap-4\">\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-cyan-50 text-black shadow-lg\">\n            #F0FCFF\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-cyan-100 text-black shadow-lg\">\n            #E6FAFE\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-cyan-200 text-black shadow-lg\">\n            #D7F8FE\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-cyan-300 text-black shadow-lg\">\n            #C3F4FD\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-cyan-400 text-black shadow-lg\">\n            #A5EEFD\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-cyan-500 text-black shadow-lg\">\n            #7EE7FC\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-cyan-600 text-white shadow-lg\">\n            #06B7DB\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-cyan-700 text-white shadow-lg\">\n            #09AACD\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-cyan-800 text-white shadow-lg\">\n            #0E8AAA\n          </div>\n          <div className=\"flex h-32 w-32 items-center justify-center rounded-medium bg-cyan-900 text-white shadow-lg\">\n            #053B48\n          </div>\n        </div>\n      </div>\n    </>\n  );\n};\n\nexport default ColorsPage;\n"
  },
  {
    "path": "app/routes/design-system+/dialog.tsx",
    "content": "import { Button } from '@nextui-org/button';\nimport { Link } from '@nextui-org/link';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from '~/components/elements/Dialog';\nimport {\n  Sheet,\n  SheetContent,\n  SheetDescription,\n  SheetTitle,\n  SheetTrigger,\n} from '~/components/elements/Sheet';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/dialog\" key=\"design-dialog\">\n      Dialog\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Dialog',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst DialogPage = () => {\n  return (\n    <>\n      <h2>Dialog</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://www.radix-ui.com/docs/primitives/components/dialog#api-reference\"\n      >\n        API Reference\n      </Link>\n      <p className=\"text-base tracking-wide md:text-lg\">Dialog</p>\n      <Dialog>\n        <DialogTrigger asChild>\n          <Button type=\"button\" variant=\"ghost\" aria-label=\"dropdown\">\n            Open\n          </Button>\n        </DialogTrigger>\n        <DialogContent>\n          <DialogHeader>\n            <DialogTitle>Are you sure absolutely sure?</DialogTitle>\n            <DialogDescription>\n              This action cannot be undone. This will permanently delete your account and remove\n              your data from our servers.\n            </DialogDescription>\n          </DialogHeader>\n        </DialogContent>\n      </Dialog>\n      <p className=\"text-base tracking-wide md:text-lg\">Sheet</p>\n      <div className=\"flex flex-row gap-x-2\">\n        <Sheet>\n          <SheetTrigger asChild>\n            <Button type=\"button\" variant=\"ghost\" aria-label=\"dropdown\">\n              Top\n            </Button>\n          </SheetTrigger>\n          <SheetContent side=\"top\" hideCloseButton>\n            <SheetTitle>Sheet Title</SheetTitle>\n            <SheetDescription>Sheet Description</SheetDescription>\n          </SheetContent>\n        </Sheet>\n        <Sheet>\n          <SheetTrigger asChild>\n            <Button type=\"button\" variant=\"ghost\" aria-label=\"dropdown\">\n              Right\n            </Button>\n          </SheetTrigger>\n          <SheetContent side=\"right\" hideCloseButton>\n            <SheetTitle>Sheet Title</SheetTitle>\n            <SheetDescription>Sheet Description</SheetDescription>\n          </SheetContent>\n        </Sheet>\n        <Sheet>\n          <SheetTrigger asChild>\n            <Button type=\"button\" variant=\"ghost\" aria-label=\"dropdown\">\n              Bottom\n            </Button>\n          </SheetTrigger>\n          <SheetContent side=\"bottom\" hideCloseButton>\n            <SheetTitle>Sheet Title</SheetTitle>\n            <SheetDescription>Sheet Description</SheetDescription>\n          </SheetContent>\n        </Sheet>\n        <Sheet>\n          <SheetTrigger asChild>\n            <Button type=\"button\" variant=\"ghost\" aria-label=\"dropdown\">\n              Left\n            </Button>\n          </SheetTrigger>\n          <SheetContent side=\"left\" hideCloseButton>\n            <SheetTitle>Sheet Title</SheetTitle>\n            <SheetDescription>Sheet Description</SheetDescription>\n          </SheetContent>\n        </Sheet>\n      </div>\n    </>\n  );\n};\n\nexport default DialogPage;\n"
  },
  {
    "path": "app/routes/design-system+/divider.tsx",
    "content": "import { Divider } from '@nextui-org/divider';\nimport { Link } from '@nextui-org/link';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/divider\" key=\"design-divider\">\n      Divider\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Divider',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst DividerPage = () => {\n  return (\n    <>\n      <h2>Divider</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://nextui-docs-v2.vercel.app/docs/components/divider#api\"\n      >\n        API Reference\n      </Link>\n      <div className=\"max-w-md\">\n        <div className=\"space-y-1\">\n          <h4>NextUI Components</h4>\n          <p className=\"opacity-70\">Beautiful, fast and modern React UI library.</p>\n        </div>\n        <Divider className=\"my-4\" />\n        <div className=\"flex h-5 items-center space-x-4 text-sm\">\n          <div>Blog</div>\n          <Divider orientation=\"vertical\" />\n          <div>Docs</div>\n          <Divider orientation=\"vertical\" />\n          <div>Source</div>\n        </div>\n      </div>\n    </>\n  );\n};\n\nexport default DividerPage;\n"
  },
  {
    "path": "app/routes/design-system+/icons.tsx",
    "content": "const IconsPage = () => {\n  return (\n    <>\n      <h2>Icons</h2>\n    </>\n  );\n};\n\nexport default IconsPage;\n"
  },
  {
    "path": "app/routes/design-system+/image.tsx",
    "content": "import { Link } from '@nextui-org/link';\nimport { MimeType } from 'remix-image';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport Image from '~/components/elements/Image';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/image\" key=\"design-image\">\n      Image\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Image',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst ImagePage = () => {\n  return (\n    <>\n      <h2>Image</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://remix-image.mcfarl.in/docs/component#optimized-component-image\"\n      >\n        Remix Image API Reference\n      </Link>\n      <Link showAnchorIcon underline=\"hover\" isExternal isBlock href=\"#\">\n        NextUI Image API Reference\n      </Link>\n      <p className=\"text-base tracking-wide md:text-lg\">Default</p>\n      <Image\n        src={'https://storiesv2.nextui.org/static/media/local-image-1.35051865.jpeg'}\n        alt=\"Image\"\n        width={400}\n        height={300}\n        radius=\"lg\"\n        loading=\"lazy\"\n        disableSkeleton\n      />\n      <p className=\"text-base tracking-wide md:text-lg\">Zoom</p>\n      <Image\n        src={'https://nextui.org/images/card-example-2.jpeg'}\n        alt=\"Image\"\n        width={400}\n        height={300}\n        isZoomed\n        radius=\"lg\"\n        loading=\"lazy\"\n        disableSkeleton={false}\n      />\n      <p className=\"text-base tracking-wide md:text-lg\">Skeleton</p>\n      <Image\n        src={'https://storiesv2.nextui.org/static/media/local-image-1.35051865.jpeg'}\n        alt=\"Image\"\n        width={400}\n        height={300}\n        radius=\"lg\"\n        loading=\"lazy\"\n        disableSkeleton={false}\n      />\n      <p className=\"text-base tracking-wide md:text-lg\">Custom With Remix Image</p>\n      <Image\n        src={'https://storiesv2.nextui.org/static/media/local-image-1.35051865.jpeg'}\n        alt=\"Image\"\n        width={400}\n        height={300}\n        radius=\"lg\"\n        loading=\"lazy\"\n        disableSkeleton={false}\n        placeholder=\"empty\"\n        options={{ contentType: MimeType.JPEG, quality: 100 }}\n        responsive={[\n          {\n            size: {\n              width: 400,\n              height: 300,\n            },\n          },\n        ]}\n      />\n    </>\n  );\n};\n\nexport default ImagePage;\n"
  },
  {
    "path": "app/routes/design-system+/input.tsx",
    "content": "import { Input } from '@nextui-org/input';\nimport { Link } from '@nextui-org/link';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/input\" key=\"design-input\">\n      Input\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Input',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst InputPage = () => {\n  return (\n    <>\n      <h2>Input</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://nextui-docs-v2.vercel.app/docs/components/input#api\"\n      >\n        API Reference\n      </Link>\n      <div className=\"flex w-full flex-wrap gap-4 md:flex-nowrap\">\n        <Input type=\"email\" label=\"Email\" />\n        <Input type=\"email\" label=\"Email\" placeholder=\"Enter your email\" />\n      </div>\n    </>\n  );\n};\n\nexport default InputPage;\n"
  },
  {
    "path": "app/routes/design-system+/pagination.tsx",
    "content": "import { Link } from '@nextui-org/link';\nimport { Pagination } from '@nextui-org/pagination';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/pagination\" key=\"design-pagination\">\n      Pagination\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Pagination',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst PaginationPage = () => {\n  return (\n    <>\n      <h2>Pagination</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://nextui-docs-v2.vercel.app/docs/components/pagination#api\"\n      >\n        API Reference\n      </Link>\n      <Pagination total={10} initialPage={1} />\n    </>\n  );\n};\n\nexport default PaginationPage;\n"
  },
  {
    "path": "app/routes/design-system+/popover.tsx",
    "content": "import { useState } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { Link } from '@nextui-org/link';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport { Popover, PopoverContent, PopoverTrigger } from '~/components/elements/Popover';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/popover\" key=\"design-popover\">\n      Popover\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Popover',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst PopoverPage = () => {\n  const [openPopover, setOpenPopover] = useState(false);\n  return (\n    <>\n      <h2>Popover</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://www.radix-ui.com/docs/primitives/components/popover#api-reference\"\n      >\n        API Reference\n      </Link>\n      <Popover open={openPopover} onOpenChange={setOpenPopover}>\n        <PopoverTrigger asChild>\n          <Button variant={openPopover ? 'flat' : 'solid'}>Open</Button>\n        </PopoverTrigger>\n        <PopoverContent className=\"!p-3\">Place content for the popover here.</PopoverContent>\n      </Popover>\n    </>\n  );\n};\n\nexport default PopoverPage;\n"
  },
  {
    "path": "app/routes/design-system+/progress.tsx",
    "content": "import { Link } from '@nextui-org/link';\nimport { Progress } from '@nextui-org/progress';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/progress\" key=\"design-progress\">\n      Progress\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Progress',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst ProgressPage = () => {\n  return (\n    <>\n      <h2>Progress</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://nextui-docs-v2.vercel.app/docs/components/progress#api\"\n      >\n        API Reference\n      </Link>\n      <Progress aria-label=\"Loading...\" value={60} className=\"max-w-md\" />\n    </>\n  );\n};\n\nexport default ProgressPage;\n"
  },
  {
    "path": "app/routes/design-system+/scroll-area.tsx",
    "content": "import { Link } from '@nextui-org/link';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport {\n  ScrollArea,\n  ScrollBar,\n  ScrollCorner,\n  ScrollViewport,\n} from '~/components/elements/ScrollArea';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/scroll-area\" key=\"design-scroll-area\">\n      Scroll Area\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Scroll Area',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst ScrollAreaPage = () => {\n  return (\n    <>\n      <h2>Scroll Area</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://www.radix-ui.com/docs/primitives/components/scroll-area#api-reference\"\n      >\n        API Reference\n      </Link>\n      <ScrollArea className=\"h-[200px] w-[350px] rounded-small border border-divider p-4\">\n        <ScrollViewport>\n          Jokester began sneaking into the castle in the middle of the night and leaving jokes all\n          over the place: under the king's pillow, in his soup, even in the royal toilet. The king\n          was furious, but he couldn't seem to stop Jokester. And then, one day, the people of the\n          kingdom discovered that the jokes left by Jokester were so funny that they couldn't help\n          but laugh. And once they started laughing, they couldn't stop.\n        </ScrollViewport>\n        <ScrollBar />\n        <ScrollCorner />\n      </ScrollArea>\n    </>\n  );\n};\n\nexport default ScrollAreaPage;\n"
  },
  {
    "path": "app/routes/design-system+/select.tsx",
    "content": "import { Link } from '@nextui-org/link';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '~/components/elements/Select';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/select\" key=\"design-select\">\n      Select\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Select',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst SelectPage = () => {\n  return (\n    <>\n      <h2>Select</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://www.radix-ui.com/docs/primitives/components/select#api-reference\"\n      >\n        API Reference\n      </Link>\n      <Select>\n        <SelectTrigger className=\"w-[180px]\">\n          <SelectValue placeholder=\"Theme\" />\n        </SelectTrigger>\n        <SelectContent>\n          <SelectItem value=\"light\">Light</SelectItem>\n          <SelectItem value=\"dark\">Dark</SelectItem>\n          <SelectItem value=\"system\">System</SelectItem>\n        </SelectContent>\n      </Select>\n      <Select>\n        <SelectTrigger className=\"w-[180px]\">\n          <SelectValue placeholder=\"Theme\" />\n        </SelectTrigger>\n        <SelectContent position=\"popper\" sideOffset={5}>\n          <SelectItem value=\"light\">Light</SelectItem>\n          <SelectItem value=\"dark\">Dark</SelectItem>\n          <SelectItem value=\"system\">System</SelectItem>\n        </SelectContent>\n      </Select>\n    </>\n  );\n};\n\nexport default SelectPage;\n"
  },
  {
    "path": "app/routes/design-system+/skeleton.tsx",
    "content": "import { Card } from '@nextui-org/card';\nimport { Link } from '@nextui-org/link';\nimport { Skeleton } from '@nextui-org/skeleton';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/skeleton\" key=\"design-skeleton\">\n      Skeleton\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Skeleton',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst SkeletonPage = () => {\n  return (\n    <>\n      <h2>Skeleton</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://nextui-docs-v2.vercel.app/docs/components/skeleton#api\"\n      >\n        API Reference\n      </Link>\n      <Card className=\"w-[200px] space-y-5 p-4\" radius=\"lg\">\n        <Skeleton className=\"rounded-medium\">\n          <div className=\"h-24 rounded-medium bg-default-300\"></div>\n        </Skeleton>\n        <div className=\"space-y-3\">\n          <Skeleton className=\"w-3/5 rounded-medium\">\n            <div className=\"h-3 w-3/5 rounded-medium bg-default-200\"></div>\n          </Skeleton>\n          <Skeleton className=\"w-4/5 rounded-medium\">\n            <div className=\"h-3 w-4/5 rounded-medium bg-default-200\"></div>\n          </Skeleton>\n          <Skeleton className=\"w-2/5 rounded-medium\">\n            <div className=\"h-3 w-2/5 rounded-medium bg-default-300\"></div>\n          </Skeleton>\n        </div>\n      </Card>\n    </>\n  );\n};\n\nexport default SkeletonPage;\n"
  },
  {
    "path": "app/routes/design-system+/slider.tsx",
    "content": "import { useState } from 'react';\nimport { Link } from '@nextui-org/link';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport Slider from '~/components/elements/Slider';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/slider\" key=\"design-slider\">\n      Slider\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Slider',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst SliderPage = () => {\n  const [horizontalValue, setHorizontalValue] = useState([33]);\n  const [verticalValue, setVerticalValue] = useState([33]);\n  const [multipleValue, setMultipleValue] = useState([33, 66]);\n  return (\n    <>\n      <h2>Slider</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://www.radix-ui.com/docs/primitives/components/slider#api-reference\"\n      >\n        API Reference\n      </Link>\n      <p className=\"text-base tracking-wide md:text-lg\">Horizontal</p>\n      <Slider\n        defaultValue={horizontalValue}\n        max={100}\n        onValueChange={setHorizontalValue}\n        showValueOnHover\n        step={1}\n        value={horizontalValue}\n      />\n      <p className=\"text-base tracking-wide md:text-lg\">Vertical</p>\n      <Slider\n        defaultValue={verticalValue}\n        max={100}\n        onValueChange={setVerticalValue}\n        showValueOnHover\n        orientation=\"vertical\"\n        step={1}\n        value={verticalValue}\n      />\n      <p className=\"text-base tracking-wide md:text-lg\">Multiple thumbs</p>\n      <Slider\n        defaultValue={multipleValue}\n        max={100}\n        onValueChange={setMultipleValue}\n        showValueOnHover\n        step={1}\n        value={multipleValue}\n      />\n      <p className=\"text-base tracking-wide md:text-lg\">Colors</p>\n      <Slider defaultValue={[33]} max={100} step={1} color=\"default\" />\n      <Slider defaultValue={[33]} max={100} step={1} color=\"primary\" />\n      <Slider defaultValue={[33]} max={100} step={1} color=\"secondary\" />\n      <Slider defaultValue={[33]} max={100} step={1} color=\"success\" />\n      <Slider defaultValue={[33]} max={100} step={1} color=\"warning\" />\n      <Slider defaultValue={[33]} max={100} step={1} color=\"danger\" />\n      <Slider defaultValue={[33]} max={100} step={1} color=\"gradient\" />\n    </>\n  );\n};\n\nexport default SliderPage;\n"
  },
  {
    "path": "app/routes/design-system+/switch.tsx",
    "content": "import { Link } from '@nextui-org/link';\nimport { Switch } from '@nextui-org/switch';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/switch\" key=\"design-switch\">\n      Switch\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Switch',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst SwitchPage = () => {\n  return (\n    <>\n      <h2>Switch</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://nextui-docs-v2.vercel.app/docs/components/switch#api\"\n      >\n        API Reference\n      </Link>\n      <Switch defaultSelected aria-label=\"Automatic updates\" />\n    </>\n  );\n};\n\nexport default SwitchPage;\n"
  },
  {
    "path": "app/routes/design-system+/tabs.tsx",
    "content": "import { Link } from '@nextui-org/link';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '~/components/elements/tab/Tabs';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/tabs\" key=\"design-tabs\">\n      Tabs\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Tabs',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst ScrollAreaPage = () => {\n  return (\n    <>\n      <h2>Tabs</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://www.radix-ui.com/docs/primitives/components/tabs#api-reference\"\n      >\n        API Reference\n      </Link>\n      <Tabs defaultValue=\"account\" orientation=\"vertical\">\n        <TabsList>\n          <TabsTrigger value=\"account\">Account</TabsTrigger>\n          <TabsTrigger value=\"password\">Password</TabsTrigger>\n        </TabsList>\n        <TabsContent value=\"account\" className=\"!min-h-fit\">\n          Make changes to your account here.\n        </TabsContent>\n        <TabsContent value=\"password\" className=\"!min-h-fit\">\n          Change your password here.\n        </TabsContent>\n      </Tabs>\n      <Tabs defaultValue=\"account\" orientation=\"horizontal\">\n        <TabsList>\n          <TabsTrigger value=\"account\">Account</TabsTrigger>\n          <TabsTrigger value=\"password\">Password</TabsTrigger>\n        </TabsList>\n        <TabsContent value=\"account\" className=\"!min-h-fit !px-0 !py-5\">\n          Make changes to your account here.\n        </TabsContent>\n        <TabsContent value=\"password\" className=\"!min-h-fit !px-0 !py-5\">\n          Change your password here.\n        </TabsContent>\n      </Tabs>\n    </>\n  );\n};\n\nexport default ScrollAreaPage;\n"
  },
  {
    "path": "app/routes/design-system+/toast.tsx",
    "content": "import { Button } from '@nextui-org/button';\nimport { Link } from '@nextui-org/link';\nimport { toast } from 'sonner';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport Info from '~/assets/icons/InfoIcon';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/toast\" key=\"design-toast\">\n      Toast\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Toast',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst ToastPage = () => {\n  return (\n    <>\n      <h2>Toast</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        href=\"https://github.com/emilkowalski/sonner#usage\"\n      >\n        API Reference\n      </Link>\n      <Button onPress={() => toast('This is a toast', { duration: Infinity })}>Show toast</Button>\n      <Button\n        onPress={() =>\n          toast('This is a toast', {\n            description: 'This is a toast description',\n            icon: <Info className=\"h-5 w-5\" />,\n            duration: Infinity,\n          })\n        }\n      >\n        Show toast with description\n      </Button>\n      <Button\n        onPress={() =>\n          toast.success('This is a success toast', {\n            description: 'This is a toast description',\n            duration: Infinity,\n          })\n        }\n      >\n        Show success toast\n      </Button>\n      <Button\n        onPress={() =>\n          toast.error('This is a error toast', {\n            description: 'This is a toast description',\n            duration: Infinity,\n          })\n        }\n      >\n        Show error toast\n      </Button>\n      <Button\n        onPress={() =>\n          toast('This is a action toast', {\n            action: {\n              label: 'Undo',\n              // eslint-disable-next-line no-console\n              onClick: () => console.log('Undo'),\n            },\n            duration: Infinity,\n          })\n        }\n      >\n        Show action toast\n      </Button>\n      <Button\n        onPress={() =>\n          toast.promise(\n            () =>\n              new Promise((resolve) => {\n                setTimeout(resolve, 2000);\n              }),\n            {\n              loading: 'Loading...',\n              success: 'Success',\n              error: 'Error',\n              duration: Infinity,\n            },\n          )\n        }\n      >\n        Show promise toast\n      </Button>\n    </>\n  );\n};\n\nexport default ToastPage;\n"
  },
  {
    "path": "app/routes/design-system+/tooltip.tsx",
    "content": "import { Button } from '@nextui-org/button';\nimport { Link } from '@nextui-org/link';\nimport { Tooltip } from '@nextui-org/tooltip';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/tooltip\" key=\"design-tooltip\">\n      Tooltip\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Tooltip',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst TooltipPage = () => {\n  return (\n    <>\n      <h2>Tooltip</h2>\n      <Link\n        showAnchorIcon\n        underline=\"hover\"\n        isExternal\n        isBlock\n        href=\"https://nextui-docs-v2.vercel.app/docs/components/tooltip#api\"\n      >\n        API Reference\n      </Link>\n      <Tooltip content=\"I am a tooltip\">\n        <Button variant=\"flat\">Hover me</Button>\n      </Tooltip>\n    </>\n  );\n};\n\nexport default TooltipPage;\n"
  },
  {
    "path": "app/routes/design-system+/typography.tsx",
    "content": "import type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/typography\" key=\"design-typography\">\n      Typography\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Typography',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst TypographyPage = () => {\n  return (\n    <>\n      <h2>Typography</h2>\n      <br />\n      <p className=\"text-base tracking-wide md:text-lg\">Display</p>\n      <span className=\"text-display\">Almost before we knew it, we had left the ground.</span>\n      {/* <p className=\"text-base tracking-wide md:text-lg\">Numeric</p>\n      <span>\n        1 AU = 1,495978707x10\n        <sup>11</sup> m\n      </span>\n      <p className=\"text-base tracking-wide md:text-lg\">Mono</p>\n      <span>console.log(foobar)</span> */}\n      <br />\n      <p className=\"text-base tracking-wide md:text-lg\">H1</p>\n      <h1>Almost before we knew it, we had left the ground.</h1>\n      <p className=\"text-base tracking-wide md:text-lg\">H2</p>\n      <h2>Almost before we knew it, we had left the ground.</h2>\n      <p className=\"text-base tracking-wide md:text-lg\">H3</p>\n      <h3>Almost before we knew it, we had left the ground.</h3>\n      <p className=\"text-base tracking-wide md:text-lg\">H4</p>\n      <h4>Almost before we knew it, we had left the ground.</h4>\n      <p className=\"text-base tracking-wide md:text-lg\">H5</p>\n      <h5>Almost before we knew it, we had left the ground.</h5>\n      <p className=\"text-base tracking-wide md:text-lg\">H6</p>\n      <h6>Almost before we knew it, we had left the ground.</h6>\n      <br />\n      <p className=\"text-base tracking-wide md:text-lg\">Text size 4xl</p>\n      <p className=\"text-4xl\">Almost before we knew it, we had left the ground.</p>\n      <p className=\"text-base tracking-wide md:text-lg\">Text size 3xl</p>\n      <p className=\"text-3xl\">Almost before we knew it, we had left the ground.</p>\n      <p className=\"text-base tracking-wide md:text-lg\">Text size 2xl</p>\n      <p className=\"text-2xl\">Almost before we knew it, we had left the ground.</p>\n      <p className=\"text-base tracking-wide md:text-lg\">Text size xl</p>\n      <p className=\"text-xl\">Almost before we knew it, we had left the ground.</p>\n      <p className=\"text-base tracking-wide md:text-lg\">Text size lg</p>\n      <p className=\"text-lg\">Almost before we knew it, we had left the ground.</p>\n      <p className=\"text-base tracking-wide md:text-lg\">Text size base</p>\n      <p className=\"text-base\">Almost before we knew it, we had left the ground.</p>\n      <p className=\"text-base tracking-wide md:text-lg\">Text size sm</p>\n      <p className=\"text-sm\">Almost before we knew it, we had left the ground.</p>\n      <p className=\"text-base tracking-wide md:text-lg\">Text size xs</p>\n      <p className=\"text-xs\">Almost before we knew it, we had left the ground.</p>\n      <br />\n      <p className=\"text-base tracking-wide md:text-lg\">Colors</p>\n      <p className=\"text-lg text-primary\">Almost before we knew it, we had left the ground.</p>\n      <p className=\"text-lg text-secondary\">Almost before we knew it, we had left the ground.</p>\n      <p className=\"text-lg text-success\">Almost before we knew it, we had left the ground.</p>\n      <p className=\"text-lg text-warning\">Almost before we knew it, we had left the ground.</p>\n      <p className=\"text-lg text-danger\">Almost before we knew it, we had left the ground.</p>\n      <p className=\"text-gradient text-lg\">Almost before we knew it, we had left the ground.</p>\n      <br />\n      <p className=\"text-base tracking-wide md:text-lg\">Strong</p>\n      <strong>Almost before we knew it, we had left the ground.</strong>\n      <p className=\"text-base tracking-wide md:text-lg\">EM</p>\n      <em>Almost before we knew it, we had left the ground.</em>\n      {/* <p className=\"text-base tracking-wide md:text-lg\">BigNum</p>\n      <span>\n        1 AU = 1,495978707x10\n        <sup>11</sup> m\n      </span>\n      <p className=\"text-base tracking-wide md:text-lg\">BigNum Outline</p>\n      <span>\n        1 AU = 1,495978707x10\n        <sup>11</sup> m\n      </span>\n      <span>\n        1 AU = 1,495978707x10\n        <sup>11</sup> m\n      </span> */}\n    </>\n  );\n};\n\nexport default TypographyPage;\n"
  },
  {
    "path": "app/routes/design-system+/video-player.tsx",
    "content": "import { json } from '@remix-run/node';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const loader = async () =>\n  json({\n    provider: 'test',\n    idProvider: 'test-player',\n    subtitles: [\n      {\n        url: 'https://www.artplayer.org/assets/sample/subtitle.srt',\n        lang: 'ch-jp',\n      },\n      {\n        url: 'https://www.artplayer.org/assets/sample/subtitle.cn.srt',\n        lang: 'ch',\n      },\n      {\n        url: 'https://www.artplayer.org/assets/sample/subtitle.jp.srt',\n        lang: 'jp',\n      },\n    ],\n    sources: [\n      {\n        quality: 360,\n        url: 'https://www.artplayer.org/assets/sample/video.mp4',\n      },\n      {\n        quality: 480,\n        url: 'https://www.artplayer.org/assets/sample/video.mp4',\n      },\n      {\n        quality: 720,\n        url: 'https://www.artplayer.org/assets/sample/video.mp4',\n      },\n      {\n        quality: 1080,\n        url: 'https://www.artplayer.org/assets/sample/video.mp4',\n      },\n    ],\n    episodeInfo: {\n      id: 'test-player-episode',\n      title: 'Your name',\n      description: \"It's a test episode\",\n      number: 1,\n      image: 'https://www.artplayer.org/assets/sample/poster.jpg',\n    },\n    hasNextEpisode: true,\n    routePlayer: '/design-system/video-player',\n    titlePlayer: 'Test Player',\n    id: 'test-player',\n    posterPlayer: 'https://www.artplayer.org/assets/sample/poster.jpg',\n  });\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system/video-player\" key=\"design-player\">\n      Player\n    </BreadcrumbItem>\n  ),\n  playerSettings: {\n    isMini: false,\n    shouldShowPlayer: true,\n  },\n  miniTitle: () => ({\n    title: 'Player',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n};\n\nconst Player = () => null;\n\nexport default Player;\n"
  },
  {
    "path": "app/routes/design-system.tsx",
    "content": "import { Outlet, useLocation } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion } from 'framer-motion';\n\nimport type { Handle } from '~/types/handle';\nimport { designSystemPages } from '~/constants/tabLinks';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Design System' },\n  { name: 'description', content: 'This page for testing the design system' },\n  { 'og:title': 'Design System' },\n  { 'og:description': 'This page for testing the design system' },\n  { 'twitter:title': 'Design System' },\n  { 'twitter:description': 'This page for testing the design system' },\n]);\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/design-system\" key=\"design-system\">\n      Design System\n    </BreadcrumbItem>\n  ),\n  showTabLink: true,\n  tabLinkPages: designSystemPages,\n  tabLinkTo: () => '/design-system',\n  miniTitle: () => ({\n    title: 'Design System',\n    showImage: false,\n  }),\n  getSitemapEntries: () => null,\n  hideSidebar: true,\n};\n\nconst DesignSystem = () => {\n  const location = useLocation();\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-start justify-center gap-y-4 px-3 sm:px-0\"\n    >\n      <Outlet />\n    </motion.div>\n  );\n};\n\nexport default DesignSystem;\n"
  },
  {
    "path": "app/routes/discover+/_index.tsx",
    "content": "import { Button } from '@nextui-org/button';\nimport { Spacer } from '@nextui-org/spacer';\nimport { useLocation, useNavigate } from '@remix-run/react';\nimport { motion } from 'framer-motion';\nimport { useTranslation } from 'react-i18next';\n\nimport Anime from '~/assets/icons/AnimeIcon';\nimport Category from '~/assets/icons/CategoryIcon';\nimport Filter from '~/assets/icons/FilterIcon';\nimport Movie from '~/assets/icons/MovieIcon';\nimport Search from '~/assets/icons/SearchIcon';\nimport TrendingUp from '~/assets/icons/TrendingUpIcon';\nimport TvShows from '~/assets/icons/TvIcon';\nimport TwoUsers from '~/assets/icons/TwoUsersIcon';\n\nconst categoryPages = [\n  {\n    name: 'movies',\n    icon: <Movie fill=\"currentColor\" />,\n    path: '/movies/popular',\n  },\n  {\n    name: 'tv-shows',\n    icon: <TvShows fill=\"currentColor\" />,\n    path: '/tv-shows/popular',\n  },\n  {\n    name: 'anime',\n    icon: <Anime fill=\"currentColor\" />,\n    path: '/anime/popular',\n  },\n  {\n    name: 'people',\n    icon: <TwoUsers fill=\"currentColor\" />,\n    path: '/people',\n  },\n  {\n    name: 'trending',\n    icon: <TrendingUp fill=\"currentColor\" />,\n    path: '/trending/',\n  },\n];\n\nconst generalPages = [\n  {\n    name: 'movie-genres',\n    path: '/genres/movie',\n    icon: <Movie fill=\"currentColor\" />,\n  },\n  {\n    name: 'tv-show-genres',\n    path: '/genres/tv',\n    icon: <TvShows fill=\"currentColor\" />,\n  },\n  {\n    name: 'anime-genres',\n    path: '/genres/anime',\n    icon: <Anime fill=\"currentColor\" />,\n  },\n  {\n    name: 'featured-lists',\n    path: '/lists',\n    icon: <Category fill=\"currentColor\" />,\n  },\n];\n\nconst DiscoverPage = () => {\n  const { t } = useTranslation('discover');\n  const navigate = useNavigate();\n  const location = useLocation();\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ x: '-10%', opacity: 0 }}\n      animate={{ x: '0', opacity: 1 }}\n      exit={{ x: '10%', opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-start justify-center px-3\"\n    >\n      <h1>{t('discover')}</h1>\n      <Spacer y={2.5} />\n      <Button\n        startContent={<Search fill=\"currentColor\" />}\n        type=\"button\"\n        fullWidth\n        onPress={() => {\n          navigate('/search/movie');\n        }}\n      >\n        {t('search')}\n      </Button>\n      <Spacer y={3} />\n      <Button\n        startContent={<Filter fill=\"currentColor\" />}\n        type=\"button\"\n        onPress={() => {\n          navigate('/discover/movies');\n        }}\n      >\n        {t('filter')}\n      </Button>\n      <Spacer y={7} />\n      <div className=\"flex w-full flex-col items-start justify-center\">\n        <h4>{t('categories')}</h4>\n        <Spacer y={2.5} />\n        <div className=\"flex w-full flex-wrap gap-x-2 gap-y-4\">\n          {categoryPages.map((page) => (\n            <Button\n              key={page.name}\n              startContent={page.icon}\n              type=\"button\"\n              onPress={() => {\n                navigate(page.path);\n              }}\n            >\n              {t(page.name)}\n            </Button>\n          ))}\n        </div>\n      </div>\n      <Spacer y={7} />\n      <div className=\"flex w-full flex-col items-start justify-center\">\n        <h4>{t('general')}</h4>\n        <Spacer y={2.5} />\n        <div className=\"flex w-full flex-wrap gap-x-2 gap-y-4\">\n          {generalPages.map((page) => (\n            <Button\n              key={page.name}\n              startContent={page.icon}\n              type=\"button\"\n              onPress={() => {\n                navigate(page.path);\n              }}\n            >\n              {t(page.name)}\n            </Button>\n          ))}\n        </div>\n      </div>\n    </motion.div>\n  );\n};\n\nexport default DiscoverPage;\n"
  },
  {
    "path": "app/routes/discover+/anime.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport type { IMedia } from '~/types/media';\nimport { getAnimeAdvancedSearch } from '~/services/consumet/anilist/anilist.server';\nimport { authenticate } from '~/services/supabase';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Discover anime' },\n  { name: 'description', content: 'Discover Anime on Sora' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/discover/anime' },\n  { property: 'og:title', content: 'Sora - Discover anime' },\n  { property: 'og:image', content: 'https://sorachill.vercel.app/api/ogimage?it=anime' },\n  { property: 'og:description', content: 'Discover Anime on Sora' },\n  { name: 'twitter:title', content: 'Sora - Discover anime' },\n  { name: 'twitter:description', content: 'Discover Anime on Sora' },\n  { name: 'twitter:image', content: 'https://sorachill.vercel.app/api/ogimage?it=anime' },\n]);\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page'));\n  if (!page && (page < 1 || page > 1000)) page = 1;\n  const query = url.searchParams.get('query') || undefined;\n  const type = (url.searchParams.get('type') as 'ANIME' | 'MANGA') || 'ANIME';\n  const season =\n    (url.searchParams.get('season') as 'WINTER' | 'SPRING' | 'SUMMER' | 'FALL') || undefined;\n  const format =\n    (url.searchParams.get('format') as\n      | 'TV'\n      | 'TV_SHORT'\n      | 'OVA'\n      | 'ONA'\n      | 'MOVIE'\n      | 'SPECIAL'\n      | 'MUSIC') || undefined;\n  const sort = url.searchParams.get('sort') || undefined;\n  const status =\n    (url.searchParams.get('status') as\n      | 'RELEASING'\n      | 'NOT_YET_RELEASED'\n      | 'FINISHED'\n      | 'CANCELLED'\n      | 'HIATUS') || undefined;\n  const genres = url.searchParams.get('genres')?.split(',') || undefined;\n  const id = url.searchParams.get('id') || undefined;\n  const year = Number(url.searchParams.get('year')) || undefined;\n\n  return json({\n    items: await getAnimeAdvancedSearch(\n      query,\n      type,\n      page,\n      20,\n      season,\n      format,\n      sort ? [sort.toUpperCase()] : undefined,\n      genres,\n      id,\n      year,\n      status,\n    ),\n  });\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/discover/anime\" key=\"discover-anime\">\n      {t('anime')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('discover'),\n    subtitle: t('anime'),\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst DiscoverAnime = () => {\n  const { items } = useLoaderData<typeof loader>();\n  const location = useLocation();\n  const navigate = useNavigate();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate('/discover/tv-shows');\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      return;\n    }\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <MediaList\n        currentPage={items?.currentPage || 1}\n        hasNextPage={items?.hasNextPage || false}\n        items={items?.results as IMedia[]}\n        itemsType=\"anime\"\n        listName={t('discover-anime')}\n        listType=\"grid\"\n        showFilterButton\n        showListTypeChangeButton\n        showSortBySelect\n      />\n    </motion.div>\n  );\n};\n\nexport default DiscoverAnime;\n"
  },
  {
    "path": "app/routes/discover+/movies.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getListDiscover } from '~/services/tmdb/tmdb.server';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Discover movies' },\n  { name: 'description', content: 'Discover Movies on Sora' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/discover/movies' },\n  { property: 'og:title', content: 'Sora - Discover movies' },\n  { property: 'og:image', content: 'https://sorachill.vercel.app/api/ogimage?it=movies' },\n  { property: 'og:description', content: 'Discover Movies on Sora' },\n  { name: 'twitter:title', content: 'Sora - Discover movies' },\n  { name: 'twitter:description', content: 'Discover Movies on Sora' },\n  { name: 'twitter:image', content: 'https://sorachill.vercel.app/api/ogimage?it=movies' },\n]);\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page')) || undefined;\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  const withGenres = url.searchParams.get('with_genres') || undefined;\n  let sortBy = url.searchParams.get('sort_by') || undefined;\n  if (sortBy && !sortBy.includes('.')) sortBy += '.desc';\n  const voteCountGte = url.searchParams.get('vote_count.gte') || 300;\n  const withOriginalLanguage = url.searchParams.get('with_original_language') || undefined;\n  const releaseDateGte = url.searchParams.get('date.gte') || undefined;\n  const releaseDateLte = url.searchParams.get('date.lte') || undefined;\n  const voteAverageGte = url.searchParams.get('vote_average.gte') || undefined;\n  const voteAverageLte = url.searchParams.get('vote_average.lte') || undefined;\n  const withRuntimeGte = url.searchParams.get('with_runtime.gte') || undefined;\n  const withRuntimeLte = url.searchParams.get('with_runtime.lte') || undefined;\n\n  return json(\n    {\n      movies: await getListDiscover(\n        'movie',\n        withGenres,\n        sortBy,\n        locale,\n        page,\n        releaseDateGte,\n        releaseDateLte,\n        undefined,\n        undefined,\n        withOriginalLanguage,\n        Number(voteCountGte),\n        voteAverageGte ? Number(voteAverageGte) : undefined,\n        voteAverageLte ? Number(voteAverageLte) : undefined,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        withRuntimeGte ? Number(withRuntimeGte) : undefined,\n        withRuntimeLte ? Number(withRuntimeLte) : undefined,\n        undefined,\n        undefined,\n      ),\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.discover,\n      },\n    },\n  );\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/discover/movies\" key=\"discover-movies\">\n      {t('movies')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('discover'),\n    subtitle: t('movies'),\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst DiscoverMovies = () => {\n  const { movies } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const location = useLocation();\n  const { t } = useTranslation();\n  const navigate = useNavigate();\n  const isHydrated = useHydrated();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      return;\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate('/discover/tv-shows');\n    }\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <MediaList\n        currentPage={movies.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={movies.items}\n        itemsType=\"movie\"\n        languages={rootData?.languages}\n        listName={t('discover-movies')}\n        listType=\"grid\"\n        showFilterButton\n        showListTypeChangeButton\n        showSortBySelect\n        totalPages={movies.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default DiscoverMovies;\n"
  },
  {
    "path": "app/routes/discover+/tv-shows.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getListDiscover } from '~/services/tmdb/tmdb.server';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Discover tv shows' },\n  { name: 'description', content: 'Discover tv shows on Sora' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/discover/tv-shows' },\n  { property: 'og:title', content: 'Sora - Discover tv shows' },\n  { property: 'og:image', content: 'https://sorachill.vercel.app/api/ogimage?it=tvshows' },\n  { property: 'og:description', content: 'Discover tv shows on Sora' },\n  { name: 'twitter:title', content: 'Sora - Discover tv shows' },\n  { name: 'twitter:description', content: 'Discover tv shows on Sora' },\n  { name: 'twitter:image', content: 'https://sorachill.vercel.app/api/ogimage?it=tvshows' },\n]);\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page')) || undefined;\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  const withGenres = url.searchParams.get('with_genres') || undefined;\n  let sortBy = url.searchParams.get('sort_by') || undefined;\n  if (sortBy && !sortBy.includes('.')) sortBy += sortBy === 'original_title' ? '.asc' : '.desc';\n  const voteCountGte = url.searchParams.get('vote_count.gte') || 300;\n  const withOriginalLanguage = url.searchParams.get('with_original_language') || undefined;\n  const withStatus = url.searchParams.get('with_status') || undefined;\n  const withType = url.searchParams.get('with_type') || undefined;\n  const firstAirDateGte = url.searchParams.get('date.gte') || undefined;\n  const firstAirDateLte = url.searchParams.get('date.lte') || undefined;\n  const voteAverageGte = url.searchParams.get('vote_average.gte') || undefined;\n  const voteAverageLte = url.searchParams.get('vote_average.lte') || undefined;\n  const withRuntimeGte = url.searchParams.get('with_runtime.gte') || undefined;\n  const withRuntimeLte = url.searchParams.get('with_runtime.lte') || undefined;\n\n  return json(\n    {\n      shows: await getListDiscover(\n        'tv',\n        withGenres,\n        sortBy,\n        locale,\n        page,\n        undefined,\n        undefined,\n        firstAirDateGte,\n        firstAirDateLte,\n        withOriginalLanguage,\n        Number(voteCountGte),\n        voteAverageGte ? Number(voteAverageGte) : undefined,\n        voteAverageLte ? Number(voteAverageLte) : undefined,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        withRuntimeGte ? Number(withRuntimeGte) : undefined,\n        withRuntimeLte ? Number(withRuntimeLte) : undefined,\n        withStatus,\n        withType,\n      ),\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.discover,\n      },\n    },\n  );\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/discover/tv-shows\" key=\"discover-tv-shows\">\n      {t('tv-shows')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('discover'),\n    subtitle: t('tv-shows'),\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst DiscoverTvShows = () => {\n  const { shows } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const location = useLocation();\n  const { t } = useTranslation();\n  const navigate = useNavigate();\n  const isHydrated = useHydrated();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate('/discover/movies');\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate('/discover/anime');\n    }\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <MediaList\n        currentPage={shows.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={shows.items}\n        itemsType=\"tv\"\n        languages={rootData?.languages}\n        listName={t('discover-tv')}\n        listType=\"grid\"\n        showFilterButton\n        showListTypeChangeButton\n        showSortBySelect\n        totalPages={shows.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default DiscoverTvShows;\n"
  },
  {
    "path": "app/routes/discover.tsx",
    "content": "import { Outlet } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport { discoverPages } from '~/constants/tabLinks';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Discover' },\n  { name: 'description', content: 'Discover Movies, TV Shows, Anime, People and More' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/discover' },\n  { property: 'og:title', content: 'Sora - Discover' },\n  { property: 'og:image', content: 'https://sorachill.vercel.app/api/ogimage?it=discover' },\n  { property: 'og:description', content: 'Discover Movies, TV Shows, Anime, People and More' },\n  { name: 'twitter:title', content: 'Sora - Discover' },\n  { name: 'twitter:description', content: 'Discover Movies, TV Shows, Anime, People and More' },\n  { name: 'twitter:image', content: 'https://sorachill.vercel.app/api/ogimage?it=discover' },\n]);\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/discover\" key=\"discover\">\n      {t('discover')}\n    </BreadcrumbItem>\n  ),\n  showTabLink: true,\n  tabLinkPages: discoverPages,\n  tabLinkTo: () => '/discover',\n  hideTabLinkWithLocation: (locationPathname: string) => {\n    if (locationPathname === '/discover') {\n      return true;\n    }\n    return false;\n  },\n  miniTitle: ({ t }) => ({\n    title: t('discover'),\n    showImage: false,\n  }),\n};\n\nconst DiscoverPage = () => <Outlet />;\n\nexport default DiscoverPage;\n"
  },
  {
    "path": "app/routes/genres+/anime.tsx",
    "content": "import { Button } from '@nextui-org/button';\nimport { Spacer } from '@nextui-org/spacer';\nimport { useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { animeGenres } from '~/constants/filterItems';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora Anime - Genres' },\n  { name: 'description', content: 'Anime Genres' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/genres/anime' },\n  { property: 'og:title', content: 'Sora Anime - Genres' },\n  { property: 'og:image', content: 'https://sorachill.vercel.app/api/ogimage?it=anime' },\n  { property: 'og:description', content: 'Anime Genres' },\n  { name: 'twitter:title', content: 'Sora Anime - Genres' },\n  { name: 'twitter:description', content: 'Anime Genres' },\n  { name: 'twitter:image', content: 'https://sorachill.vercel.app/api/ogimage?it=anime' },\n]);\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/genres/anime\" key=\"genres-anime\">\n      {t('anime')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('genres'),\n    subtitle: t('anime'),\n    showImage: false,\n  }),\n};\n\nconst AnimeGenresPage = () => {\n  const navigate = useNavigate();\n  const location = useLocation();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation('genres');\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate('/genres/tv');\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      return;\n    }\n  };\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <h4>{t('anime-genres')}</h4>\n      <Spacer y={5} />\n      <div className=\"grid grid-cols-1 justify-center gap-3 xs:grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6\">\n        {animeGenres.map((genre) => (\n          <Button\n            key={genre}\n            type=\"button\"\n            variant=\"flat\"\n            color=\"primary\"\n            onPress={() => navigate(`/discover/anime?genres=${genre}`)}\n          >\n            {genre}\n          </Button>\n        ))}\n      </div>\n    </motion.div>\n  );\n};\n\nexport default AnimeGenresPage;\n"
  },
  {
    "path": "app/routes/genres+/movie.tsx",
    "content": "import { Button } from '@nextui-org/button';\nimport { Spacer } from '@nextui-org/spacer';\nimport { useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora Movies - Genres' },\n  { name: 'description', content: 'Movies Genres' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/genres/movies' },\n  { property: 'og:title', content: 'Sora Movies - Genres' },\n  { property: 'og:image', content: 'https://sorachill.vercel.app/api/ogimage?it=movies' },\n  { property: 'og:description', content: 'Movies Genres' },\n  { name: 'twitter:title', content: 'Sora Movies - Genres' },\n  { name: 'twitter:description', content: 'Movies Genres' },\n  { name: 'twitter:image', content: 'https://sorachill.vercel.app/api/ogimage?it=movies' },\n]);\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/genres/movie\" key=\"genres-movie\">\n      {t('movie')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('genres'),\n    subtitle: t('movie'),\n    showImage: false,\n  }),\n};\n\nconst MovieGenresPage = () => {\n  const navigate = useNavigate();\n  const { genresMovie } = useTypedRouteLoaderData('root');\n  const { t } = useTranslation('genres');\n  const location = useLocation();\n  const isHydrated = useHydrated();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      return;\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate('/genres/tv');\n    }\n  };\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <h4>{t('movie-genres')}</h4>\n      <Spacer y={5} />\n      <div className=\"grid grid-cols-1 justify-center gap-3 xs:grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6\">\n        {Object.entries(genresMovie).map(([id, name]) => (\n          <Button\n            key={id}\n            type=\"button\"\n            variant=\"flat\"\n            color=\"primary\"\n            onPress={() => navigate(`/discover/movies?with_genres=${id}`)}\n          >\n            {name}\n          </Button>\n        ))}\n      </div>\n    </motion.div>\n  );\n};\n\nexport default MovieGenresPage;\n"
  },
  {
    "path": "app/routes/genres+/tv.tsx",
    "content": "import { Button } from '@nextui-org/button';\nimport { Spacer } from '@nextui-org/spacer';\nimport { useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora Tv shows - Genres' },\n  { name: 'description', content: 'Tv shows Genres' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/genres/tv' },\n  { property: 'og:title', content: 'Sora Tv shows - Genres' },\n  { property: 'og:image', content: 'https://sorachill.vercel.app/api/ogimage?it=tvshows' },\n  { property: 'og:description', content: 'Tv shows Genres' },\n  { name: 'twitter:title', content: 'Sora Tv shows - Genres' },\n  { name: 'twitter:description', content: 'Tv shows Genres' },\n  { name: 'twitter:image', content: 'https://sorachill.vercel.app/api/ogimage?it=tvshows' },\n]);\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/genres/tv\" key=\"genres-tv\">\n      {t('tv-shows')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('genres'),\n    subtitle: t('tv-shows'),\n    showImage: false,\n  }),\n};\n\nconst TvGenresPage = () => {\n  const navigate = useNavigate();\n  const { genresTv } = useTypedRouteLoaderData('root');\n  const { t } = useTranslation('genres');\n  const location = useLocation();\n  const isHydrated = useHydrated();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate('/genres/movie');\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate('/genres/anime');\n    }\n  };\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <h4>{t('tv-show-genres')}</h4>\n      <Spacer y={5} />\n      <div className=\"grid grid-cols-1 justify-center gap-3 xs:grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6\">\n        {Object.entries(genresTv).map(([id, name]) => (\n          <Button\n            key={id}\n            color=\"primary\"\n            type=\"button\"\n            variant=\"flat\"\n            onPress={() => navigate(`/discover/tv-shows?with_genres=${id}`)}\n          >\n            {name}\n          </Button>\n        ))}\n      </div>\n    </motion.div>\n  );\n};\n\nexport default TvGenresPage;\n"
  },
  {
    "path": "app/routes/genres.tsx",
    "content": "import { Outlet } from '@remix-run/react';\n\nimport type { Handle } from '~/types/handle';\nimport { genrePages } from '~/constants/tabLinks';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/genres\" key=\"genres\">\n      {t('genres')}\n    </BreadcrumbItem>\n  ),\n  showTabLink: true,\n  tabLinkPages: genrePages,\n  tabLinkTo: () => '/genres',\n  miniTitle: ({ t }) => ({\n    title: t('genres'),\n    showImage: false,\n  }),\n};\n\nconst GenresPage = () => <Outlet />;\n\nexport default GenresPage;\n"
  },
  {
    "path": "app/routes/lists+/$listId.tsx",
    "content": "import { useRef } from 'react';\nimport { Pagination } from '@nextui-org/pagination';\nimport { Spacer } from '@nextui-org/spacer';\nimport { useMediaQuery } from '@react-hookz/web';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion } from 'framer-motion';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getListDetail } from '~/services/tmdb/tmdb.server';\nimport type { IList } from '~/services/tmdb/tmdb.types';\nimport useSplitArrayIntoPage from '~/utils/react/hooks/useSplitArrayIntoPage';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta<typeof loader>(({ data, params }) => {\n  if (!data) {\n    return [\n      { title: 'Missing List' },\n      { name: 'description', content: `There is no list with id ${params.listId}` },\n    ];\n  }\n  const { detail } = data;\n  return [\n    { title: `Sora - ${detail?.name || ''}` },\n    { name: 'description', content: `${detail?.description || ''}` },\n    { name: 'keywords', content: `${detail?.name || ''}` },\n    {\n      property: 'og:url',\n      content: `https://sorachill.vercel.app/lists/${params.listId}`,\n    },\n    { property: 'og:title', content: `Sora - ${detail?.name || ''}` },\n    { property: 'og:description', content: `${detail?.description || ''}` },\n    { name: 'twitter:title', content: `Sora - ${detail?.name || ''}` },\n    { name: 'twitter:description', content: `${detail?.description || ''}` },\n  ];\n});\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const { listId } = params;\n  const cid = Number(listId);\n\n  return json(\n    {\n      detail: await getListDetail(cid, locale),\n    },\n    { headers: { 'Cache-Control': CACHE_CONTROL.collection } },\n  );\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <>\n      <BreadcrumbItem to=\"/lists/\" key=\"lists\">\n        {t('featured-lists')}\n      </BreadcrumbItem>\n      <BreadcrumbItem to={`/lists/${match.params.listId}`} key={`lists-${match.params.listId}`}>\n        {(match.data as { detail: IList })?.detail?.name || match.params.listId}\n      </BreadcrumbItem>\n    </>\n  ),\n  miniTitle: ({ match, t }) => ({\n    title: (match.data as { detail: IList })?.detail?.name || t('featured-lists'),\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst CollectionDetail = () => {\n  const { detail } = useLoaderData<typeof loader>();\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const rootData = useTypedRouteLoaderData('root');\n  const ref = useRef<HTMLDivElement>(null);\n  const location = useLocation();\n  const { gotoPage, currentPage, maxPage, currentData } = useSplitArrayIntoPage(\n    detail?.items || [],\n    20,\n  );\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ x: '-10%', opacity: 0 }}\n      animate={{ x: '0', opacity: 1 }}\n      exit={{ y: '-10%', opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 pb-16 sm:px-0\"\n    >\n      <div ref={ref} />\n      <MediaList\n        listType=\"grid\"\n        showListTypeChangeButton\n        items={currentData}\n        listName={detail?.name}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        itemsType=\"movie-tv\"\n      />\n      <Spacer y={5} />\n      {maxPage > 1 && (\n        <div className=\"mt-7 flex flex-row items-center\">\n          <Pagination\n            // showControls={!isSm}\n            total={maxPage}\n            initialPage={currentPage}\n            // shadow\n            onChange={(page) => {\n              gotoPage(page);\n              ref.current?.scrollIntoView({\n                behavior: 'instant',\n                block: 'center',\n                inline: 'nearest',\n              });\n            }}\n            {...(isSm && { size: 'sm' })}\n          />\n        </div>\n      )}\n    </motion.div>\n  );\n};\n\nexport default CollectionDetail;\n"
  },
  {
    "path": "app/routes/lists+/_index.tsx",
    "content": "import { useRef } from 'react';\nimport { Pagination } from '@nextui-org/pagination';\nimport { Spacer } from '@nextui-org/spacer';\nimport { useMediaQuery } from '@react-hookz/web';\nimport { useLocation } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion } from 'framer-motion';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport useSplitArrayIntoPage from '~/utils/react/hooks/useSplitArrayIntoPage';\nimport featuredList from '~/constants/featuredList';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Featured Lists' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/lists/' },\n  { property: 'og:title', content: 'Sora - Featured Lists' },\n  { name: 'twitter:title', content: 'Sora - Featured Lists' },\n]);\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/lists/\" key=\"lists\">\n      {t('featured-lists')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('featured-lists'),\n    showImage: false,\n  }),\n};\n\nconst FeaturedLists = () => {\n  const location = useLocation();\n  const { t } = useTranslation();\n  const ref = useRef<HTMLDivElement>(null);\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const { gotoPage, currentPage, maxPage, currentData } = useSplitArrayIntoPage(featuredList, 12);\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ x: '-10%', opacity: 0 }}\n      animate={{ x: '0', opacity: 1 }}\n      exit={{ y: '-10%', opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n    >\n      <div ref={ref} />\n      <MediaList\n        listType=\"grid\"\n        listName={t('featured-lists')}\n        isCoverCard\n        coverItem={currentData}\n      />\n      <Spacer y={5} />\n      {maxPage > 1 && (\n        <div className=\"mt-7 flex flex-row justify-center\">\n          <Pagination\n            // showControls={!isSm}\n            total={maxPage}\n            initialPage={currentPage}\n            // shadow\n            onChange={(page) => {\n              gotoPage(page);\n              ref.current?.scrollIntoView({\n                behavior: 'smooth',\n                block: 'start',\n                inline: 'center',\n              });\n            }}\n            {...(isSm && { size: 'sm' })}\n          />\n        </div>\n      )}\n    </motion.div>\n  );\n};\n\nexport default FeaturedLists;\n"
  },
  {
    "path": "app/routes/movies+/$movieId+/_index.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { Link, useLoaderData, useNavigate, useParams } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as movieIdLoader } from '~/routes/movies+/$movieId';\nimport { authenticate } from '~/services/supabase';\nimport { getCredits, getRecommendation, getSimilar, getVideos } from '~/services/tmdb/tmdb.server';\nimport { postFetchDataHandler } from '~/services/tmdb/utils.server';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const { movieId } = params;\n  const mid = Number(movieId);\n\n  if (!mid) throw new Response('Not Found', { status: 404 });\n\n  const [similar, videos, credits, recommendations] = await Promise.all([\n    getSimilar('movie', mid),\n    getVideos('movie', mid),\n    getCredits('movie', mid),\n    getRecommendation('movie', mid),\n  ]);\n\n  if (!similar || !videos || !credits || !recommendations)\n    throw new Response('Not Found', { status: 404 });\n\n  return json(\n    {\n      videos,\n      similar,\n      recommendations,\n      topBilledCast: credits &&\n        credits.cast && [...postFetchDataHandler(credits.cast.slice(0, 9), 'people')],\n      directors: credits && credits.crew && credits.crew.filter(({ job }) => job === 'Director'),\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.detail,\n      },\n    },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader, { 'routes/movies+/$movieId': typeof movieIdLoader }>(\n  ({ matches, params }) => {\n    const movieData = matches.find((match) => match.id === 'routes/movies+/$movieId')?.data;\n    if (!movieData) {\n      return [\n        { title: 'Missing Movie' },\n        { name: 'description', content: `There is no movie with ID: ${params.movieId}` },\n      ];\n    }\n    const { detail } = movieData;\n    const { title } = detail || {};\n    return [\n      { title: `Sora - ${title}` },\n      { property: 'og:title', content: `Sora - ${title}` },\n      { property: 'og:url', content: `https://sorachill.vercel.app/movies/${params.movieId}/` },\n      { property: 'twitter:title', content: `Sora - ${title}` },\n    ];\n  },\n);\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/movies/${match.params.movieId}/`}\n      key={`movies-${match.params.movieId}-overview`}\n    >\n      {t('overview')}\n    </BreadcrumbItem>\n  ),\n};\n\nconst MovieOverview = () => {\n  const { similar, recommendations, topBilledCast, directors } = useLoaderData<typeof loader>();\n  const movieData = useTypedRouteLoaderData('routes/movies+/$movieId');\n  const rootData = useTypedRouteLoaderData('root');\n  const detail = movieData && movieData.detail;\n  const navigate = useNavigate();\n  const { movieId } = useParams();\n  const { t } = useTranslation();\n  const onClickViewMore = (type: 'cast' | 'similar' | 'recommendations') => {\n    navigate(`/movies/${detail?.id}/${type}`);\n  };\n  return (\n    <div className=\"mt-3 flex w-full max-w-[1920px] flex-col gap-x-0 gap-y-4 px-3 sm:flex-row sm:items-stretch sm:justify-center sm:gap-x-4 sm:gap-y-0 sm:px-3.5 xl:px-4 2xl:px-5\">\n      <div className=\"flex w-full grow-0 flex-col sm:w-1/3 sm:items-center sm:justify-start\">\n        <div className=\"flex w-full flex-col items-start justify-center gap-y-4 rounded-large bg-content1 p-4 nextui-sm:w-3/4 xl:w-1/2\">\n          <div className=\"flex w-full flex-row items-center justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n            <h6 className=\"grow-0 basis-1/3\">{t('original-title')}</h6>\n            <p className=\"grow\">{detail?.original_title}</p>\n          </div>\n          <div className=\"flex w-full flex-row items-center justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n            <h6 className=\"grow-0 basis-1/3\">{t('status')}</h6>\n            <p className=\"grow\">{detail?.status}</p>\n          </div>\n          <div className=\"flex w-full flex-row items-start justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n            <h6 className=\"grow-0 basis-1/3\">{t('production-companies')}</h6>\n            <div className=\"flex grow flex-col\">\n              {detail?.production_companies &&\n                detail.production_companies.map((company) => (\n                  <p key={`network-item-${company.id}`}>{company?.name}</p>\n                ))}\n            </div>\n          </div>\n          <div className=\"flex w-full flex-row items-center justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n            <h6 className=\"grow-0 basis-1/3\">{t('original-language')}</h6>\n            <p className=\"grow\">\n              {rootData?.languages?.find((lang) => lang.iso_639_1 === detail?.original_language)\n                ?.english_name || detail?.original_language}\n            </p>\n          </div>\n          <div className=\"flex w-full flex-row items-center justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n            <h6 className=\"grow-0 basis-1/3\">{t('budget')}</h6>\n            <p className=\"grow\">\n              {detail?.budget\n                ? `$${detail?.budget?.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')}`\n                : '-'}\n            </p>\n          </div>\n          <div className=\" flex w-full flex-row items-center justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n            <h6 className=\"grow-0 basis-1/3\">{t('revenue')}</h6>\n            <p className=\"grow\">\n              {detail?.revenue\n                ? `$${detail?.revenue?.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')}`\n                : '-'}\n            </p>\n          </div>\n        </div>\n      </div>\n      <div className=\"flex w-full flex-col sm:w-2/3\">\n        <div className=\"flex flex-col items-start justify-start gap-y-4 rounded-large bg-content1 p-4\">\n          <p className=\"text-justify\">{detail?.overview}</p>\n          <div className=\"flex flex-col flex-wrap gap-x-0 gap-y-4 sm:flex-row sm:gap-x-8\">\n            {directors && directors.length > 0 ? (\n              <div className=\"flex w-full flex-row items-start justify-start gap-x-4 sm:w-fit sm:flex-col\">\n                <h6 className=\"grow-0 basis-1/3 sm:basis-auto\">{t('director')}</h6>\n                <div className=\"flex grow flex-col\">\n                  {directors.map((director) => (\n                    <Link\n                      key={`director-item-${director.id}`}\n                      to={`/people/${director.id}/`}\n                      style={{ lineHeight: '1.75rem' }}\n                      className=\"text-foreground hover:text-primary focus:outline-none focus:ring-2 focus:ring-focus\"\n                    >\n                      {director.name}\n                    </Link>\n                  ))}\n                </div>\n              </div>\n            ) : null}\n            {detail?.production_countries && detail.production_countries.length > 0 ? (\n              <div className=\"flex w-full flex-row items-start justify-start gap-x-4 sm:w-fit sm:flex-col\">\n                <h6 className=\"grow-0 basis-1/3 sm:basis-auto\">{t('production-countries')}</h6>\n                <div className=\"flex grow flex-col\">\n                  {detail?.production_countries.map((country, index) => (\n                    <p key={`country-item-${index}`}>{country.name}</p>\n                  ))}\n                </div>\n              </div>\n            ) : null}\n            {detail?.spoken_languages && detail.spoken_languages.length > 0 ? (\n              <div className=\"flex w-full flex-row items-start justify-start gap-x-4 sm:w-fit sm:flex-col\">\n                <h6 className=\"grow-0 basis-1/3 sm:basis-auto\">{t('spoken-languages')}</h6>\n                <div className=\"flex grow flex-col\">\n                  {detail?.spoken_languages.map((language, index) => (\n                    <p key={`language-item-${index}`}>{language.english_name}</p>\n                  ))}\n                </div>\n              </div>\n            ) : null}\n          </div>\n        </div>\n        {topBilledCast && topBilledCast.length > 0 ? (\n          <MediaList\n            items={topBilledCast}\n            itemsType=\"people\"\n            key={`movie-top-cast-${movieId}`}\n            listName={t('top-cast')}\n            listType=\"slider-card\"\n            navigationButtons\n            onClickViewMore={() => onClickViewMore('cast')}\n            showMoreList\n          />\n        ) : null}\n        {recommendations && recommendations.items && recommendations.items.length > 0 ? (\n          <MediaList\n            genresMovie={rootData?.genresMovie}\n            genresTv={rootData?.genresTv}\n            items={recommendations.items}\n            itemsType=\"movie\"\n            key={`movie-recommendations-${movieId}`}\n            listName={t('recommendations')}\n            listType=\"slider-card\"\n            navigationButtons\n            onClickViewMore={() => onClickViewMore('recommendations')}\n            showMoreList\n          />\n        ) : null}\n        {similar && similar.items && similar.items.length > 0 ? (\n          <MediaList\n            genresMovie={rootData?.genresMovie}\n            genresTv={rootData?.genresTv}\n            items={similar.items}\n            itemsType=\"movie\"\n            key={`movie-similar-${movieId}`}\n            listName={t('similar-movies')}\n            listType=\"slider-card\"\n            navigationButtons\n            onClickViewMore={() => onClickViewMore('similar')}\n            showMoreList\n          />\n        ) : null}\n      </div>\n    </div>\n  );\n};\n\nexport default MovieOverview;\n"
  },
  {
    "path": "app/routes/movies+/$movieId+/cast.tsx",
    "content": "import { useRef } from 'react';\nimport { Pagination } from '@nextui-org/pagination';\nimport { useMediaQuery } from '@react-hookz/web';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as movieIdLoader } from '~/routes/movies+/$movieId';\nimport { authenticate } from '~/services/supabase';\nimport { getCredits } from '~/services/tmdb/tmdb.server';\nimport type { IMovieDetail } from '~/services/tmdb/tmdb.types';\nimport { postFetchDataHandler } from '~/services/tmdb/utils.server';\nimport TMDB from '~/utils/media';\nimport useSplitArrayIntoPage from '~/utils/react/hooks/useSplitArrayIntoPage';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const { movieId } = params;\n  const mid = Number(movieId);\n\n  if (!mid) throw new Response('Not found', { status: 404 });\n  const credits = await getCredits('movie', mid);\n\n  if (!credits) throw new Response('Not found', { status: 404 });\n\n  return json(\n    { cast: [...postFetchDataHandler(credits.cast, 'people')] },\n    { headers: { 'Cache-Control': CACHE_CONTROL.detail } },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader, { 'routes/movies+/$movieId': typeof movieIdLoader }>(\n  ({ params, matches }) => {\n    const movieData = matches.find((match) => match.id === 'routes/movies+/$movieId')?.data;\n    if (!movieData) {\n      return [\n        { title: 'Missing Movie' },\n        { name: 'description', content: `There is no movie with the ID: ${params.movieId}` },\n      ];\n    }\n    const { detail } = movieData;\n    const { title } = detail || {};\n    const movieTitle = title || '';\n    return [\n      { title: `Sora - ${movieTitle} - Cast` },\n      {\n        property: 'og:url',\n        content: `https://sorachill.vercel.app/movies/${params.movieId}/cast`,\n      },\n      { property: 'og:title', content: `Sora - ${movieTitle} - Cast` },\n      { name: 'twitter:title', content: `Sora - ${movieTitle} - Cast` },\n    ];\n  },\n);\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/movies/${match.params.movieId}/cast`}\n      key={`movies-${match.params.movieId}-cast`}\n    >\n      {t('cast')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch ? (parentMatch.data as { detail: IMovieDetail })?.detail?.title || '' : '',\n    subtitle: t('cast'),\n    showImage: parentMatch\n      ? (parentMatch.data as { detail: IMovieDetail })?.detail?.poster_path !== undefined\n      : false,\n    imageUrl: parentMatch\n      ? TMDB?.posterUrl(\n          (parentMatch.data as { detail: IMovieDetail })?.detail?.poster_path || '',\n          'w92',\n        )\n      : undefined,\n  }),\n};\n\nconst MovieCastPage = () => {\n  const { cast } = useLoaderData<typeof loader>();\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const ref = useRef<HTMLDivElement>(null);\n  const { gotoPage, currentPage, maxPage, currentData } = useSplitArrayIntoPage(cast || [], 20);\n\n  return (\n    <div className=\"mt-3 flex w-full max-w-[1920px] flex-col gap-y-4 px-3 sm:px-3.5 xl:px-4 2xl:px-5\">\n      <div ref={ref} />\n      <MediaList items={currentData} itemsType=\"people\" listType=\"grid\" />\n      {maxPage > 1 ? (\n        <div className=\"mt-7 flex flex-row justify-center\">\n          <Pagination\n            // showControls={!isSm}\n            total={maxPage}\n            initialPage={currentPage}\n            // shadow\n            onChange={(page) => {\n              gotoPage(page);\n              ref.current?.scrollIntoView({\n                behavior: 'instant',\n                block: 'center',\n                inline: 'nearest',\n              });\n            }}\n            {...(isSm && { size: 'sm' })}\n          />\n        </div>\n      ) : null}\n    </div>\n  );\n};\n\nexport default MovieCastPage;\n"
  },
  {
    "path": "app/routes/movies+/$movieId+/crew.tsx",
    "content": "import { useRef } from 'react';\nimport { Pagination } from '@nextui-org/pagination';\nimport { useMediaQuery } from '@react-hookz/web';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as movieIdLoader } from '~/routes/movies+/$movieId';\nimport { authenticate } from '~/services/supabase';\nimport { getCredits } from '~/services/tmdb/tmdb.server';\nimport type { IMovieDetail } from '~/services/tmdb/tmdb.types';\nimport { postFetchDataHandler } from '~/services/tmdb/utils.server';\nimport TMDB from '~/utils/media';\nimport useSplitArrayIntoPage from '~/utils/react/hooks/useSplitArrayIntoPage';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const { movieId } = params;\n  const mid = Number(movieId);\n\n  if (!mid) throw new Response('Not found', { status: 404 });\n  const credits = await getCredits('movie', mid);\n\n  if (!credits) throw new Response('Not found', { status: 404 });\n\n  return json(\n    { crew: [...postFetchDataHandler(credits.crew, 'people')] },\n    { headers: { 'Cache-Control': CACHE_CONTROL.detail } },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader, { 'routes/movies+/$movieId': typeof movieIdLoader }>(\n  ({ params, matches }) => {\n    const movieData = matches.find((match) => match.id === 'routes/movies+/$movieId')?.data;\n    if (!movieData) {\n      return [\n        { title: 'Missing Movie' },\n        { name: 'description', content: `There is no movie with the ID: ${params.movieId}` },\n      ];\n    }\n    const { detail } = movieData;\n    const { title } = detail || {};\n    const movieTitle = title || '';\n    return [\n      { title: `Sora - ${movieTitle} - Crew` },\n      {\n        property: 'og:url',\n        content: `https://sorachill.vercel.app/movies/${params.movieId}/crew`,\n      },\n      { property: 'og:title', content: `Sora - ${movieTitle} - Crew` },\n      { name: 'twitter:title', content: `Sora - ${movieTitle} - Crew` },\n    ];\n  },\n);\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/movies/${match.params.movieId}/crew`}\n      key={`movies-${match.params.movieId}-crew`}\n    >\n      {t('crew')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch ? (parentMatch.data as { detail: IMovieDetail })?.detail?.title || '' : '',\n    subtitle: t('crew'),\n    showImage: parentMatch\n      ? (parentMatch.data as { detail: IMovieDetail })?.detail?.poster_path !== undefined\n      : false,\n    imageUrl: parentMatch\n      ? TMDB?.posterUrl(\n          (parentMatch.data as { detail: IMovieDetail })?.detail?.poster_path || '',\n          'w92',\n        )\n      : undefined,\n  }),\n};\n\nconst MovieCrewPage = () => {\n  const { crew } = useLoaderData<typeof loader>();\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const ref = useRef<HTMLDivElement>(null);\n  const { gotoPage, currentPage, maxPage, currentData } = useSplitArrayIntoPage(crew || [], 20);\n\n  return (\n    <div className=\"mt-3 flex w-full max-w-[1920px] flex-col gap-y-4 px-3 sm:px-3.5 xl:px-4 2xl:px-5\">\n      <div ref={ref} />\n      <MediaList items={currentData} itemsType=\"people\" listType=\"grid\" />\n      {maxPage > 1 ? (\n        <div className=\"mt-7 flex flex-row justify-center\">\n          <Pagination\n            // showControls={!isSm}\n            total={maxPage}\n            initialPage={currentPage}\n            // shadow\n            onChange={(page) => {\n              gotoPage(page);\n              ref.current?.scrollIntoView({\n                behavior: 'instant',\n                block: 'center',\n                inline: 'nearest',\n              });\n            }}\n            {...(isSm && { size: 'sm' })}\n          />\n        </div>\n      ) : null}\n    </div>\n  );\n};\n\nexport default MovieCrewPage;\n"
  },
  {
    "path": "app/routes/movies+/$movieId+/photos.tsx",
    "content": "import * as React from 'react';\nimport { Spacer } from '@nextui-org/spacer';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { useTranslation } from 'react-i18next';\nimport { Gallery, Item, type GalleryProps } from 'react-photoswipe-gallery';\nimport { MimeType } from 'remix-image';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as movieIdLoader } from '~/routes/movies+/$movieId';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getImages } from '~/services/tmdb/tmdb.server';\nimport type { IMovieDetail } from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport Image from '~/components/elements/Image';\n\nexport const meta = mergeMeta<typeof loader, { 'routes/movies+/$movieId': typeof movieIdLoader }>(\n  ({ params, matches }) => {\n    const movieData = matches.find((match) => match.id === 'routes/movies+/$movieId')?.data;\n    if (!movieData) {\n      return [\n        { title: 'Missing Movie' },\n        { name: 'description', content: `There is no movie with the ID: ${params.movieId}` },\n      ];\n    }\n    const { detail } = movieData;\n    const { title } = detail || {};\n    const movieTitle = title || '';\n    return [\n      { title: `Sora - ${movieTitle} - Photos` },\n      {\n        property: 'og:url',\n        content: `https://sorachill.vercel.app/movies/${params.movieId}/photos`,\n      },\n      { property: 'og:title', content: `Sora - ${movieTitle} - Photos` },\n      { name: 'twitter:title', content: `Sora - ${movieTitle} - Photos` },\n    ];\n  },\n);\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const { movieId } = params;\n  const mid = Number(movieId);\n  if (!mid) throw new Response('Not Found', { status: 404 });\n\n  const images = await getImages('movie', mid, locale);\n  if (!images) throw new Response('Not Found', { status: 404 });\n\n  return json({ images }, { headers: { 'Cache-Control': CACHE_CONTROL.detail } });\n};\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/movies/${match.params.movieId}/photos`}\n      key={`movies-${match.params.movieId}-photos`}\n    >\n      {t('photos')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch ? (parentMatch.data as { detail: IMovieDetail })?.detail?.title || '' : '',\n    subtitle: t('photos'),\n    showImage: parentMatch\n      ? (parentMatch.data as { detail: IMovieDetail })?.detail?.poster_path !== undefined\n      : false,\n    imageUrl: parentMatch\n      ? TMDB?.posterUrl(\n          (parentMatch.data as { detail: IMovieDetail })?.detail?.poster_path || '',\n          'w92',\n        )\n      : undefined,\n  }),\n};\n\nconst uiElements: GalleryProps['uiElements'] = [\n  {\n    name: 'custom-rotate-button',\n    ariaLabel: 'Rotate',\n    order: 9,\n    isButton: true,\n    html: {\n      isCustomSVG: true,\n      inner:\n        '<path d=\"M13.887 6.078C14.258 6.234 14.5 6.598 14.5 7V8.517C18.332 8.657 21.258 10.055 23.15 12.367 24.519 14.041 25.289 16.13 25.496 18.409A1 1 0 0123.504 18.591C23.327 16.645 22.68 14.952 21.601 13.633 20.156 11.867 17.831 10.653 14.5 10.517V12A1.002 1.002 0 0112.779 12.693L10.304 10.121A1.002 1.002 0 0110.324 8.713L12.8 6.286A1 1 0 0113.887 6.078ZM7.5 16A1.5 1.5 0 006 17.5V24.5A1.5 1.5 0 007.5 26H17.5A1.5 1.5 0 0019 24.5V17.5A1.5 1.5 0 0017.5 16H7.5Z\" id=\"pswp__icn-rotate\"/>',\n      outlineID: 'pswp__icn-rotate',\n    },\n    appendTo: 'bar',\n    onClick: (_, __, pswpInstance) => {\n      const item = pswpInstance.currSlide?.content.element;\n\n      const prevRotateAngle = Number(item?.dataset.rotateAngel) || 0;\n      const rotateAngle = prevRotateAngle === 270 ? 0 : prevRotateAngle + 90;\n\n      // add slide rotation\n      if (item) {\n        item.style.transform = `${item.style.transform?.replace(\n          `rotate(-${prevRotateAngle}deg)`,\n          '',\n        )} rotate(-${rotateAngle}deg)`;\n        item.dataset.rotateAngel = String(rotateAngle);\n      }\n    },\n    onInit: (_, pswpInstance) => {\n      // remove applied rotation on slide change\n      // https://photoswipe.com/events/#slide-content-events\n      pswpInstance.on('contentRemove', () => {\n        const item = pswpInstance.currSlide?.content.element;\n        if (item) {\n          item.style.transform = `${item.style.transform?.replace(\n            `rotate(-${item.dataset.rotateAngel || 0}deg)`,\n            '',\n          )}`;\n          delete item.dataset.rotateAngel;\n        }\n      });\n    },\n  },\n];\n\nconst MoviePhotosPage = () => {\n  const { images } = useLoaderData<typeof loader>();\n  const { t } = useTranslation();\n  const movieData = useTypedRouteLoaderData('routes/movies+/$movieId');\n  return (\n    <div className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\">\n      <Spacer y={5} />\n      {images?.backdrops && images.backdrops.length > 0 && (\n        <>\n          <h5 className=\"flex w-full justify-center\">\n            <strong>{t('backdrops')}</strong>\n          </h5>\n          <Spacer y={2.5} />\n          <Gallery withCaption withDownloadButton uiElements={uiElements}>\n            <div className=\"grid grid-cols-1 justify-center gap-3 xs:grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6\">\n              {images?.backdrops?.map((image) => (\n                <Item\n                  key={image.file_path}\n                  cropped\n                  original={TMDB.profileUrl(image?.file_path, 'original')}\n                  thumbnail={TMDB.profileUrl(image?.file_path, 'w185')}\n                  alt={`Backdrop of ${movieData?.detail?.title} image size ${image.width}x${image.height}`}\n                  caption={`Backdrop of ${movieData?.detail?.title} size ${image.width}x${image.height}`}\n                  width={image.width}\n                  height={image.height}\n                >\n                  {({ ref, open }) => (\n                    <Image\n                      src={TMDB.profileUrl(image?.file_path, 'w185')}\n                      ref={ref as React.MutableRefObject<HTMLImageElement>}\n                      onClick={open}\n                      alt={`Backdrop of ${movieData?.detail?.title} image size ${image.width}x${image.height}`}\n                      radius=\"lg\"\n                      classNames={{\n                        img: 'h-auto min-w-[120px] cursor-pointer object-cover 2xs:min-w-[185px]',\n                      }}\n                      title={movieData?.detail?.title}\n                      placeholder=\"empty\"\n                      options={{\n                        contentType: MimeType.WEBP,\n                      }}\n                    />\n                  )}\n                </Item>\n              ))}\n            </div>\n          </Gallery>\n          <Spacer y={5} />\n        </>\n      )}\n      {images?.logos && images.logos.length > 0 && (\n        <>\n          <h5 className=\"flex w-full justify-center\">\n            <strong>{t('logos')}</strong>\n          </h5>\n          <Spacer y={2.5} />\n          <Gallery withCaption withDownloadButton uiElements={uiElements}>\n            <div className=\"grid grid-cols-1 gap-3 xs:grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6\">\n              {images?.logos?.map((image) => (\n                <Item\n                  key={image.file_path}\n                  cropped\n                  original={TMDB.logoUrl(image?.file_path, 'original')}\n                  thumbnail={TMDB.logoUrl(image?.file_path, 'w185')}\n                  alt={`Logo of ${movieData?.detail?.title} image size ${image.width}x${image.height}`}\n                  caption={`Logo of ${movieData?.detail?.title} size ${image.width}x${image.height}`}\n                  width={image.width}\n                  height={image.height}\n                >\n                  {({ ref, open }) => (\n                    <Image\n                      src={TMDB.logoUrl(image?.file_path, 'w185')}\n                      ref={ref as React.MutableRefObject<HTMLImageElement>}\n                      onClick={open}\n                      alt={`Logo of ${movieData?.detail?.title} image size ${image.width}x${image.height}`}\n                      radius=\"lg\"\n                      classNames={{\n                        img: 'h-auto min-w-[120px] cursor-pointer object-cover 2xs:min-w-[185px]',\n                      }}\n                      title={movieData?.detail?.title}\n                      placeholder=\"empty\"\n                      options={{\n                        contentType: MimeType.WEBP,\n                      }}\n                    />\n                  )}\n                </Item>\n              ))}\n            </div>\n          </Gallery>\n          <Spacer y={5} />\n        </>\n      )}\n      {images?.posters && images.posters.length > 0 && (\n        <>\n          <h5 className=\"flex w-full justify-center\">\n            <strong>{t('posters')}</strong>\n          </h5>\n          <Spacer y={2.5} />\n          <Gallery withCaption withDownloadButton uiElements={uiElements}>\n            <div className=\"grid grid-cols-1 gap-3 xs:grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6\">\n              {images?.posters?.map((image) => (\n                <Item\n                  key={image.file_path}\n                  cropped\n                  original={TMDB.profileUrl(image?.file_path, 'original')}\n                  thumbnail={TMDB.profileUrl(image?.file_path, 'w185')}\n                  alt={`Poster of ${movieData?.detail?.title} image size ${image.width}x${image.height}`}\n                  caption={`Poster of ${movieData?.detail?.title} size ${image.width}x${image.height}`}\n                  width={image.width}\n                  height={image.height}\n                >\n                  {({ ref, open }) => (\n                    <Image\n                      src={TMDB.profileUrl(image?.file_path, 'w185')}\n                      ref={ref as React.MutableRefObject<HTMLImageElement>}\n                      onClick={open}\n                      alt={`Poster of ${movieData?.detail?.title} image size ${image.width}x${image.height}`}\n                      radius=\"lg\"\n                      classNames={{\n                        img: 'h-auto min-w-[120px] cursor-pointer object-cover 2xs:min-w-[185px]',\n                      }}\n                      loading=\"lazy\"\n                      title={movieData?.detail?.title}\n                      placeholder=\"empty\"\n                      options={{\n                        contentType: MimeType.WEBP,\n                      }}\n                    />\n                  )}\n                </Item>\n              ))}\n            </div>\n          </Gallery>\n        </>\n      )}\n    </div>\n  );\n};\n\nexport default MoviePhotosPage;\n"
  },
  {
    "path": "app/routes/movies+/$movieId+/recommendations.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as movieIdLoader } from '~/routes/movies+/$movieId';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getRecommendation } from '~/services/tmdb/tmdb.server';\nimport type { IMovieDetail } from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const { movieId } = params;\n  const mid = Number(movieId);\n  if (!mid) throw new Response('Not Found', { status: 404 });\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page')) || undefined;\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  const recommendations = await getRecommendation('movie', mid, page, locale);\n  if (!recommendations) throw new Response('Not Found', { status: 404 });\n\n  return json(\n    { recommendations },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.detail },\n    },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader, { 'routes/movies+/$movieId': typeof movieIdLoader }>(\n  ({ params, matches }) => {\n    const movieData = matches.find((match) => match.id === 'routes/movies+/$movieId')?.data;\n    if (!movieData) {\n      return [\n        { title: 'Missing Movie' },\n        { name: 'description', content: `There is no movie with the ID: ${params.movieId}` },\n      ];\n    }\n    const { detail } = movieData;\n    const { title } = detail || {};\n    const movieTitle = title || '';\n    return [\n      { title: `Sora - ${movieTitle} - Recommendations` },\n      {\n        property: 'og:url',\n        content: `https://sorachill.vercel.app/movies/${params.movieId}/recommendations`,\n      },\n      { property: 'og:title', content: `Sora - ${movieTitle} - Recommendations` },\n      { name: 'twitter:title', content: `Sora - ${movieTitle} - Recommendations` },\n    ];\n  },\n);\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/movies/${match.params.movieId}/recommendations`}\n      key={`movies-${match.params.movieId}-recommendations`}\n    >\n      {t('recommendations')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch ? (parentMatch.data as { detail: IMovieDetail })?.detail?.title || '' : '',\n    subtitle: t('recommendations'),\n    showImage: parentMatch\n      ? (parentMatch.data as { detail: IMovieDetail })?.detail?.poster_path !== undefined\n      : false,\n    imageUrl: parentMatch\n      ? TMDB?.posterUrl(\n          (parentMatch.data as { detail: IMovieDetail })?.detail?.poster_path || '',\n          'w92',\n        )\n      : undefined,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst MovieRecommendationsPage = () => {\n  const { recommendations } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const { t } = useTranslation();\n\n  return (\n    <div className=\"mt-3 flex w-full max-w-[1920px] flex-col gap-y-4 px-3 sm:px-3.5 xl:px-4 2xl:px-5\">\n      <MediaList\n        currentPage={recommendations?.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        itemsType=\"movie\"\n        items={recommendations?.items}\n        listName={t('recommendations')}\n        listType=\"grid\"\n        scrollToTopListAfterChangePage\n        showListTypeChangeButton\n        totalPages={recommendations?.totalPages}\n      />\n    </div>\n  );\n};\n\nexport default MovieRecommendationsPage;\n"
  },
  {
    "path": "app/routes/movies+/$movieId+/similar.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as movieIdLoader } from '~/routes/movies+/$movieId';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getSimilar } from '~/services/tmdb/tmdb.server';\nimport type { IMovieDetail } from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const { movieId } = params;\n  const mid = Number(movieId);\n  if (!mid) throw new Response('Not Found', { status: 404 });\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page')) || undefined;\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  const similar = await getSimilar('movie', mid, page, locale);\n  if (!similar) throw new Response('Not Found', { status: 404 });\n\n  return json(\n    { similar },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.detail },\n    },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader, { 'routes/movies+/$movieId': typeof movieIdLoader }>(\n  ({ params, matches }) => {\n    const movieData = matches.find((match) => match.id === 'routes/movies+/$movieId')?.data;\n    if (!movieData) {\n      return [\n        { title: 'Missing Movie' },\n        { name: 'description', content: `There is no movie with the ID: ${params.movieId}` },\n      ];\n    }\n    const { detail } = movieData;\n    const { title } = detail || {};\n    const movieTitle = title || '';\n    return [\n      { title: `Sora - ${movieTitle} - Similar` },\n      {\n        property: 'og:url',\n        content: `https://sorachill.vercel.app/movies/${params.movieId}/similar`,\n      },\n      { property: 'og:title', content: `Sora - ${movieTitle} - Similar` },\n      { name: 'twitter:title', content: `Sora - ${movieTitle} - Similar` },\n    ];\n  },\n);\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/movies/${match.params.movieId}/similar`}\n      key={`movies-${match.params.movieId}-similar`}\n    >\n      {t('similar')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch ? (parentMatch.data as { detail: IMovieDetail })?.detail?.title || '' : '',\n    subtitle: t('similar'),\n    showImage: parentMatch\n      ? (parentMatch.data as { detail: IMovieDetail })?.detail?.poster_path !== undefined\n      : false,\n    imageUrl: parentMatch\n      ? TMDB?.posterUrl(\n          (parentMatch.data as { detail: IMovieDetail })?.detail?.poster_path || '',\n          'w92',\n        )\n      : undefined,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst MovieSimilarPage = () => {\n  const { similar } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const { t } = useTranslation();\n\n  return (\n    <div className=\"mt-3 flex w-full max-w-[1920px] flex-col gap-y-4 px-3 sm:px-3.5 xl:px-4 2xl:px-5\">\n      <MediaList\n        currentPage={similar?.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={similar?.items}\n        itemsType=\"movie\"\n        listName={t('similar-movies')}\n        listType=\"grid\"\n        scrollToTopListAfterChangePage\n        showListTypeChangeButton\n        totalPages={similar?.totalPages}\n      />\n    </div>\n  );\n};\n\nexport default MovieSimilarPage;\n"
  },
  {
    "path": "app/routes/movies+/$movieId+/videos.tsx",
    "content": "import { useEffect, useRef, useState } from 'react';\nimport { Card, CardBody, CardFooter } from '@nextui-org/card';\nimport { useMediaQuery } from '@react-hookz/web';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useFetcher, useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { AnimatePresence, motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\nimport { MimeType } from 'remix-image';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as movieIdLoader } from '~/routes/movies+/$movieId';\nimport { authenticate } from '~/services/supabase';\nimport { getVideos } from '~/services/tmdb/tmdb.server';\nimport type { IMovieDetail } from '~/services/tmdb/tmdb.types';\nimport type { IYoutubeItem } from '~/services/youtube/youtube.types';\nimport TMDB from '~/utils/media';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport { Dialog, DialogContent, DialogTrigger } from '~/components/elements/Dialog';\nimport WatchTrailer, { type Trailer } from '~/components/elements/dialog/WatchTrailerDialog';\nimport Image from '~/components/elements/Image';\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '~/components/elements/tab/Tabs';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const { movieId } = params;\n  const mid = Number(movieId);\n\n  if (!mid) throw new Response('Not Found', { status: 404 });\n  const videos = await getVideos('movie', mid);\n\n  return json({ videos }, { headers: { 'Cache-Control': CACHE_CONTROL.detail } });\n};\n\nexport const meta = mergeMeta<typeof loader, { 'routes/movies+/$movieId': typeof movieIdLoader }>(\n  ({ params, matches }) => {\n    const movieData = matches.find((match) => match.id === 'routes/movies+/$movieId')?.data;\n    if (!movieData) {\n      return [\n        { title: 'Missing Movie' },\n        { name: 'description', content: `There is no movie with the ID: ${params.movieId}` },\n      ];\n    }\n    const { detail } = movieData;\n    const { title } = detail || {};\n    const movieTitle = title || '';\n    return [\n      { title: `Sora - ${movieTitle} - Videos` },\n      {\n        property: 'og:url',\n        content: `https://sorachill.vercel.app/movies/${params.movieId}/videos`,\n      },\n      { property: 'og:title', content: `Sora - ${movieTitle} - Videos` },\n      { name: 'twitter:title', content: `Sora - ${movieTitle} - Videos` },\n    ];\n  },\n);\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/movies/${match.params.movieId}/videos`}\n      key={`movies-${match.params.movieId}-videos`}\n    >\n      {t('videos')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch ? (parentMatch.data as { detail: IMovieDetail })?.detail?.title || '' : '',\n    subtitle: t('videos'),\n    showImage: parentMatch\n      ? (parentMatch.data as { detail: IMovieDetail })?.detail?.poster_path !== undefined\n      : false,\n    imageUrl: parentMatch\n      ? TMDB?.posterUrl(\n          (parentMatch.data as { detail: IMovieDetail })?.detail?.poster_path || '',\n          'w92',\n        )\n      : undefined,\n  }),\n};\n\nconst MovieVideosPage = () => {\n  const { videos } = useLoaderData<typeof loader>();\n  const fetcher = useFetcher<{ youtubeVideo: IYoutubeItem[] }>();\n  const { t } = useTranslation();\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const [activeType, setActiveType] = useState('trailer');\n  const [activeTypeVideos, setActiveTypeVideos] = useState<IYoutubeItem[] | []>([]);\n  const [visible, setVisible] = useState(false);\n  const [trailer, setTrailer] = useState<Trailer>({});\n  const underlineRef = useRef<HTMLDivElement>(null);\n  const typesVideo = [\n    {\n      id: 'trailer',\n      activeVideo: 'trailer',\n    },\n    {\n      id: 'teaser',\n      activeVideo: 'teaser',\n    },\n    {\n      id: 'clip',\n      activeVideo: 'clip',\n    },\n    {\n      id: 'behind_the_scenes',\n      activeVideo: 'behind-the-scenes',\n    },\n    {\n      id: 'bloopers',\n      activeVideo: 'bloopers',\n    },\n    {\n      id: 'featurette',\n      activeVideo: 'featurette',\n    },\n  ];\n  useEffect(() => {\n    if (videos) {\n      let activeVideo = [];\n      const activeTypeVideo = typesVideo.find((item) => item.id === activeType);\n      activeVideo = videos.results?.filter((video) => video.type === activeTypeVideo?.activeVideo);\n      const keyVideo = activeVideo.map((item) => item.key).join(',');\n      if (keyVideo) fetcher.load(`/api/youtube-video?id=${keyVideo}`);\n      else setActiveTypeVideos([]);\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [activeType, videos]);\n  useEffect(() => {\n    if (fetcher.data && fetcher.data.youtubeVideo) {\n      // @ts-expect-error\n      setActiveTypeVideos(fetcher?.data?.youtubeVideo as IYoutubeItem[]);\n    }\n  }, [fetcher.data]);\n\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  const handleDragEnd = (event: MouseEvent | PointerEvent | TouchEvent, info: PanInfo) => {\n    const currentTab = typesVideo.find((type) => type.id === activeType);\n    if (info.offset?.x > 100) {\n      // swipe right\n      if (currentTab?.id === 'trailer') {\n        setActiveType('featurette');\n      } else {\n        const index = typesVideo.findIndex((type) => type.id === activeType);\n        setActiveType(typesVideo[index - 1].id);\n      }\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      // swipe left\n      if (currentTab?.id === 'featurette') {\n        setActiveType('trailer');\n      } else {\n        const index = typesVideo.findIndex((type) => type.id === activeType);\n        setActiveType(typesVideo[index + 1].id);\n      }\n    }\n  };\n\n  return (\n    <div className=\"mt-3 flex w-full max-w-[1920px] flex-col gap-x-0 gap-y-4 px-3 sm:flex-row sm:items-stretch sm:justify-center sm:gap-x-4 sm:gap-y-0 sm:px-3.5 xl:px-4 2xl:px-5\">\n      <Tabs\n        defaultValue={activeType}\n        value={activeType}\n        orientation={isSm ? 'horizontal' : 'vertical'}\n        onValueChange={(value) => setActiveType(value)}\n        className=\"w-full\"\n      >\n        <TabsList>\n          {typesVideo.map((type) => (\n            <TabsTrigger key={type.id} value={type.id} className=\"relative\">\n              <h6 className=\"!m-0\">{t(type.activeVideo)}</h6>\n              {activeType === type.id ? (\n                <motion.div\n                  className=\"absolute overflow-hidden rounded-small bg-default-foreground data-[orientation=horizontal]:bottom-0 data-[orientation=vertical]:left-0 data-[orientation=horizontal]:h-1 data-[orientation=vertical]:h-1/2 data-[orientation=horizontal]:w-1/2 data-[orientation=vertical]:w-1\"\n                  layoutId=\"video-underline\"\n                  data-orientation={isSm ? 'horizontal' : 'vertical'}\n                  ref={underlineRef}\n                />\n              ) : null}\n            </TabsTrigger>\n          ))}\n        </TabsList>\n        <Dialog open={visible} onOpenChange={setVisible}>\n          <AnimatePresence mode=\"wait\">\n            <TabsContent value={activeType}>\n              <motion.div\n                initial={{ opacity: 0 }}\n                animate={{ opacity: 1 }}\n                exit={{ opacity: 0 }}\n                transition={{ ease: 'easeInOut', duration: 0.3 }}\n                drag={isMobile ? 'x' : false}\n                dragConstraints={{ left: 0, right: 0 }}\n                dragElastic={0.4}\n                onDragEnd={handleDragEnd}\n                dragDirectionLock\n              >\n                <div className=\"grid w-full grid-cols-1 justify-items-center gap-4 lg:grid-cols-2 3xl:grid-cols-3 4xl:grid-cols-4\">\n                  {activeTypeVideos\n                    ? activeTypeVideos.map((video) => (\n                        <DialogTrigger asChild key={video?.id}>\n                          <Card\n                            isPressable\n                            isHoverable\n                            role=\"figure\"\n                            className=\"w-[320px] data-[hover=true]:ring-2 data-[hover=true]:ring-focus\"\n                            onPress={() => {\n                              const videoPlay = videos?.results?.find(\n                                (item) => item.key === video.id,\n                              );\n                              if (videoPlay) {\n                                setTrailer(videoPlay);\n                              }\n                            }}\n                          >\n                            <CardBody className=\"shrink-0 grow-0 overflow-hidden p-0\">\n                              <Image\n                                src={video?.snippet?.thumbnails?.medium?.url}\n                                width={320}\n                                height={180}\n                                alt={video?.snippet?.title}\n                                loading=\"lazy\"\n                                title={video?.snippet?.title}\n                                placeholder=\"empty\"\n                                options={{ contentType: MimeType.WEBP }}\n                                responsive={[{ size: { width: 320, height: 180 } }]}\n                              />\n                            </CardBody>\n                            <CardFooter className=\"flex flex-col items-start justify-start\">\n                              <h6 className=\"!m-0 text-left font-semibold\">\n                                {video?.snippet?.title}\n                              </h6>\n                              <p className=\"opacity-70\">{video?.snippet?.channelTitle}</p>\n                            </CardFooter>\n                          </Card>\n                        </DialogTrigger>\n                      ))\n                    : null}\n                </div>\n              </motion.div>\n            </TabsContent>\n          </AnimatePresence>\n          <DialogContent className=\"overflow-hidden !p-0\">\n            <WatchTrailer trailer={trailer} />\n          </DialogContent>\n        </Dialog>\n      </Tabs>\n    </div>\n  );\n};\n\nexport default MovieVideosPage;\n"
  },
  {
    "path": "app/routes/movies+/$movieId.tsx",
    "content": "import { useEffect, useRef } from 'react';\nimport { useIntersectionObserver } from '@react-hookz/web';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { Outlet, useLoaderData, useLocation } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, useTransform } from 'framer-motion';\nimport Vibrant from 'node-vibrant';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getImdbRating, getMovieDetail } from '~/services/tmdb/tmdb.server';\nimport type { IMovieDetail } from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport useColorDarkenLighten from '~/utils/react/hooks/useColorDarkenLighten';\nimport { useCustomHeaderChangePosition } from '~/utils/react/hooks/useHeader';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport { useHeaderStyle } from '~/store/layout/useHeaderStyle';\nimport { useLayout } from '~/store/layout/useLayout';\nimport { movieTvDetailsPages } from '~/constants/tabLinks';\nimport { MediaBackgroundImage, MediaDetail } from '~/components/media/MediaDetail';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport ErrorBoundaryView from '~/components/elements/shared/ErrorBoundaryView';\nimport TabLink from '~/components/elements/tab/TabLink';\nimport { backgroundStyles } from '~/components/styles/primitives';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const { movieId } = params;\n  const mid = Number(movieId);\n  if (!mid) throw new Response('Not Found', { status: 404 });\n  const detail = await getMovieDetail(mid, locale);\n  if (!detail) throw new Response('Not Found', { status: 404 });\n  const extractColorImageUrl =\n    process.env.NODE_ENV === 'development'\n      ? TMDB.backdropUrl(detail?.backdrop_path || detail?.poster_path || '', 'w300')\n      : `https://corsproxy.io/?${encodeURIComponent(\n          TMDB.backdropUrl(detail?.backdrop_path || detail?.poster_path || '', 'w300'),\n        )}`;\n  let titleEng =\n    detail?.original_language === 'en'\n      ? detail?.original_title\n      : locale === 'en'\n      ? detail?.title\n      : '';\n  if (detail?.original_language !== 'en' && locale !== 'en') {\n    const [detailEng, imdbRating, fimg] = await Promise.all([\n      getMovieDetail(mid, 'en-US'),\n      detail?.imdb_id && process.env.IMDB_API_URL !== undefined\n        ? getImdbRating(detail?.imdb_id)\n        : undefined,\n      fetch(extractColorImageUrl),\n    ]);\n    titleEng = detailEng?.title || '';\n    const fimgb = Buffer.from(await fimg.arrayBuffer());\n    const palette =\n      (detail?.backdrop_path || detail?.poster_path) && fimgb\n        ? await Vibrant.from(fimgb).getPalette()\n        : undefined;\n    return json(\n      {\n        detail: {\n          ...detail,\n          color: palette\n            ? Object.values(palette).sort((a, b) =>\n                a?.population === undefined || b?.population === undefined\n                  ? 0\n                  : b.population - a.population,\n              )[0]?.hex\n            : undefined,\n          titleEng,\n        },\n        imdbRating,\n        palette,\n      },\n      {\n        headers: {\n          'Cache-Control': CACHE_CONTROL.movie,\n        },\n      },\n    );\n  }\n  const [imdbRating, fimg] = await Promise.all([\n    detail?.imdb_id && process.env.IMDB_API_URL !== undefined\n      ? getImdbRating(detail?.imdb_id)\n      : undefined,\n    fetch(extractColorImageUrl),\n  ]);\n  const fimgb = Buffer.from(await fimg.arrayBuffer());\n  const palette =\n    detail?.backdrop_path || detail?.poster_path\n      ? await Vibrant.from(fimgb).getPalette()\n      : undefined;\n  return json(\n    {\n      detail: {\n        ...detail,\n        color: palette\n          ? Object.values(palette).sort((a, b) =>\n              a?.population === undefined || b?.population === undefined\n                ? 0\n                : b.population - a.population,\n            )[0]?.hex\n          : undefined,\n        titleEng,\n      },\n      imdbRating,\n      translations: undefined,\n      palette,\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.movie,\n      },\n    },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader>(({ data, params }) => {\n  if (!data) {\n    return [];\n  }\n  const { detail } = data;\n  const { title, overview } = detail || {};\n  const movieTitle = title || '';\n  return [\n    { name: 'description', content: overview },\n    { property: 'og:description', content: overview },\n    { name: 'twitter:description', content: overview },\n    {\n      property: 'og:image',\n      content: `https://sorachill.vercel.app/api/ogimage?m=${params.movieId}&mt=movie`,\n    },\n    {\n      name: 'keywords',\n      content: `Watch ${movieTitle}, Stream ${movieTitle}, Watch ${movieTitle} HD, Online ${movieTitle}, Streaming ${movieTitle}, English, Subtitle ${movieTitle}, English Subtitle`,\n    },\n    {\n      name: 'twitter:image',\n      content: `https://sorachill.vercel.app/api/ogimage?m=${params.movieId}&mt=movie`,\n    },\n  ];\n});\n\nexport const handle: Handle = {\n  breadcrumb: ({ match }) => (\n    <BreadcrumbItem to={`/movies/${match.params.movieId}`} key={`movies-${match.params.movieId}`}>\n      {(match.data as { detail: IMovieDetail })?.detail?.title || match.params.movieId}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ match, t }) => ({\n    title: (match.data as { detail: IMovieDetail })?.detail?.title || '',\n    subtitle: t('overview'),\n    showImage: (match.data as { detail: IMovieDetail })?.detail?.poster_path !== undefined,\n    imageUrl: TMDB?.posterUrl(\n      (match.data as { detail: IMovieDetail })?.detail?.poster_path || '',\n      'w92',\n    ),\n  }),\n  preventScrollToTop: true,\n  disableLayoutPadding: true,\n  customHeaderBackgroundColor: true,\n  customHeaderChangeColorOnScroll: true,\n};\n\nconst MovieDetail = () => {\n  const { detail, imdbRating } = useLoaderData<typeof loader>();\n  const { state } = useLocation();\n  const isHydrated = useHydrated();\n  const { backgroundColor } = useColorDarkenLighten(detail?.color);\n  const { sidebarBoxedMode } = useSoraSettings();\n  const { viewportRef, scrollY } = useLayout((scrollState) => scrollState);\n  const { setBackgroundColor, startChangeScrollPosition } = useHeaderStyle(\n    (headerState) => headerState,\n  );\n  const paddingTop = useTransform(\n    scrollY,\n    [0, startChangeScrollPosition, startChangeScrollPosition + 100],\n    [16, 16, startChangeScrollPosition ? 0 : 16],\n  );\n  const paddingBottom = useTransform(\n    scrollY,\n    [0, startChangeScrollPosition, startChangeScrollPosition + 100],\n    [32, 32, startChangeScrollPosition ? 0 : 32],\n  );\n  const tabLinkRef = useRef<HTMLDivElement>(null);\n  const tablinkIntersection = useIntersectionObserver(tabLinkRef, {\n    root: viewportRef,\n    rootMargin: sidebarBoxedMode ? '-180px 0px 0px 0px' : '-165px 0px 0px 0px',\n    threshold: [0.5],\n  });\n\n  useCustomHeaderChangePosition(tablinkIntersection);\n\n  useEffect(() => {\n    if (startChangeScrollPosition) {\n      setBackgroundColor(backgroundColor);\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [backgroundColor, startChangeScrollPosition]);\n\n  const currentTime = state && (state as { currentTime: number }).currentTime;\n  const backdropPath = detail?.backdrop_path\n    ? TMDB?.backdropUrl(detail?.backdrop_path || '', 'w1280')\n    : undefined;\n\n  return (\n    <>\n      <MediaBackgroundImage backdropPath={backdropPath} backgroundColor={backgroundColor} />\n      <div className=\"relative top-[-80px] w-full sm:top-[-200px]\">\n        <MediaDetail\n          type=\"movie\"\n          item={detail}\n          imdbRating={imdbRating}\n          color={detail.color}\n          trailerTime={currentTime}\n        />\n        <div className=\"flex w-full flex-col items-center justify-center\">\n          <motion.div\n            className=\"sticky top-[61px] z-[1000] flex w-full justify-center transition-[padding] duration-100 ease-in-out\"\n            style={{\n              backgroundColor: isHydrated ? backgroundColor : 'transparent',\n              paddingTop,\n              paddingBottom,\n            }}\n            ref={tabLinkRef}\n          >\n            <div\n              className={backgroundStyles({ tablink: true })}\n              style={{ backgroundColor: isHydrated ? backgroundColor : 'transparent' }}\n            />\n            <TabLink pages={movieTvDetailsPages} linkTo={`/movies/${detail?.id}`} />\n          </motion.div>\n          <Outlet />\n        </div>\n      </div>\n    </>\n  );\n};\n\nexport function ErrorBoundary() {\n  return (\n    <ErrorBoundaryView\n      statusHandlers={{\n        404: ({ params }) => <p>There is no movie with the ID: {params.movieId}</p>,\n      }}\n    />\n  );\n}\n\nexport default MovieDetail;\n"
  },
  {
    "path": "app/routes/movies+/$movieId_.watch.tsx",
    "content": "import type { ISource } from '@consumet/extensions';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport Vibrant from 'node-vibrant';\n\nimport type { Handle } from '~/types/handle';\nimport {\n  getKissKhEpisodeStream,\n  getKissKhEpisodeSubtitle,\n  getKissKhInfo,\n} from '~/services/kisskh/kisskh.server';\nimport { loklokGetMovieInfo } from '~/services/loklok';\nimport { LOKLOK_URL } from '~/services/loklok/utils.server';\nimport { authenticate, insertHistory } from '~/services/supabase';\nimport {\n  // getTranslations,\n  getImdbRating,\n  getMovieDetail,\n  getRecommendation,\n  getWatchEpisode,\n} from '~/services/tmdb/tmdb.server';\nimport type { IMovieDetail } from '~/services/tmdb/tmdb.types';\nimport { TMDB as TmdbUtils } from '~/services/tmdb/utils.server';\n// import i18next from '~/i18n/i18next.server';\nimport TMDB from '~/utils/media';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport ErrorBoundaryView from '~/components/elements/shared/ErrorBoundaryView';\nimport WatchDetail from '~/components/elements/shared/WatchDetail';\n\nexport const meta = mergeMeta<typeof loader>(({ data, params }) => {\n  if (!data) {\n    return [\n      { title: 'Missing Movie' },\n      { name: 'description', content: `There is no movie with the ID: ${params.movieId}` },\n    ];\n  }\n  const { detail } = data || {};\n  const { title, overview } = detail || {};\n  const movieTitle = title || '';\n  return [\n    { title: `Sora - Watch ${movieTitle}` },\n    { name: 'description', content: overview },\n    {\n      name: 'keywords',\n      content: `Watch ${movieTitle}, Stream ${movieTitle}, Watch ${movieTitle} HD, Online ${movieTitle}, Streaming ${movieTitle}, English, Subtitle ${movieTitle}, English Subtitle`,\n    },\n    { property: 'og:url', content: `https://sorachill.vercel.app/movies/${params.movieId}/watch` },\n    { property: 'og:title', content: `Sora - Watch ${movieTitle}` },\n    { property: 'og:description', content: overview },\n    {\n      property: 'og:image',\n      content: `https://sorachill.vercel.app/api/ogimage?m=${params.movieId}&mt=movie`,\n    },\n    { name: 'twitter:title', content: `Sora - Watch ${movieTitle}` },\n    { name: 'twitter:description', content: overview },\n    {\n      name: 'twitter:image',\n      content: `https://sorachill.vercel.app/api/ogimage?m=${params.movieId}&mt=movie`,\n    },\n  ];\n});\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const user = await authenticate(request, true, true, true);\n\n  const url = new URL(request.url);\n  const provider = url.searchParams.get('provider');\n  const idProvider = url.searchParams.get('id');\n  const routePlayer = `${url.pathname}${url.search}`;\n  const { movieId } = params;\n  const mid = Number(movieId);\n  if (!mid || !provider || !idProvider) throw new Response('Not Found', { status: 404 });\n  const [detail, recommendations] = await Promise.all([\n    getMovieDetail(mid),\n    getRecommendation('movie', mid),\n  ]);\n  if (!detail) throw new Response('Not Found', { status: 404 });\n  const titlePlayer = detail?.title || detail?.original_title || '';\n  const posterPlayer = TmdbUtils.backdropUrl(detail?.backdrop_path || '', 'w1280');\n  const subtitleOptions = {\n    tmdb_id: detail?.id,\n    type: 'movie',\n    title: detail?.title,\n    sub_format: provider === 'KissKh' ? 'srt' : 'webvtt',\n  };\n  const overview = detail?.overview || undefined;\n  const extractColorImageUrl =\n    process.env.NODE_ENV === 'development'\n      ? TMDB.backdropUrl(detail?.backdrop_path || detail?.poster_path || '', 'w300')\n      : `https://corsproxy.io/?${encodeURIComponent(\n          TMDB.backdropUrl(detail?.backdrop_path || detail?.poster_path || '', 'w300'),\n        )}`;\n\n  if (user) {\n    insertHistory({\n      user_id: user.id,\n      media_type: 'movie',\n      duration: (detail?.runtime || 0) * 60,\n      watched: 0,\n      route: url.pathname + url.search,\n      media_id: (detail?.id || mid).toString(),\n      poster: TMDB.backdropUrl(detail?.backdrop_path || '', 'w300'),\n      title: detail?.title || detail?.original_title || undefined,\n      overview: detail?.overview || undefined,\n    });\n  }\n\n  if (provider === 'Loklok') {\n    if (!idProvider) throw new Response('Id Not Found', { status: 404 });\n    const [movieDetail, imdbRating, fimg] = await Promise.all([\n      loklokGetMovieInfo(idProvider),\n      detail?.imdb_id && process.env.IMDB_API_URL !== undefined\n        ? getImdbRating(detail?.imdb_id)\n        : undefined,\n      fetch(extractColorImageUrl),\n    ]);\n    const fimgb = Buffer.from(await fimg.arrayBuffer());\n    const palette =\n      (detail?.backdrop_path || detail?.poster_path) && fimgb\n        ? await Vibrant.from(fimgb).getPalette()\n        : undefined;\n    return json(\n      {\n        provider,\n        detail,\n        recommendations,\n        imdbRating,\n        sources: movieDetail?.sources,\n        subtitles: movieDetail?.subtitles.map((sub) => ({\n          lang: `${sub.language} (${sub.lang})`,\n          url: `${LOKLOK_URL}/subtitle?url=${sub.url}`,\n        })),\n        userId: user?.id,\n        routePlayer,\n        titlePlayer,\n        id: mid,\n        posterPlayer,\n        typeVideo: 'movie',\n        subtitleOptions,\n        overview,\n        color: palette\n          ? Object.values(palette).sort((a, b) =>\n              a?.population === undefined || b?.population === undefined\n                ? 0\n                : b.population - a.population,\n            )[0]?.hex\n          : undefined,\n      },\n      {\n        headers: {\n          'Cache-Control': CACHE_CONTROL.watch,\n        },\n      },\n    );\n  }\n\n  if (provider === 'Flixhq') {\n    if (!idProvider) throw new Response('Id Not Found', { status: 404 });\n    const getId = (id: string) => {\n      const idArr = id.split('-');\n      return idArr[idArr.length - 1];\n    };\n    const [movieStreamLink, imdbRating, fimg] = await Promise.all([\n      getWatchEpisode(idProvider, getId(idProvider)),\n      detail?.imdb_id && process.env.IMDB_API_URL !== undefined\n        ? getImdbRating(detail?.imdb_id)\n        : undefined,\n      fetch(extractColorImageUrl),\n    ]);\n    const fimgb = Buffer.from(await fimg.arrayBuffer());\n    const palette =\n      detail?.backdrop_path || detail?.poster_path\n        ? await Vibrant.from(fimgb).getPalette()\n        : undefined;\n    return json(\n      {\n        provider,\n        detail,\n        recommendations,\n        imdbRating,\n        sources: (movieStreamLink as ISource)?.sources,\n        subtitles: (movieStreamLink as ISource)?.subtitles,\n        userId: user?.id,\n        routePlayer,\n        titlePlayer,\n        id: mid,\n        posterPlayer,\n        typeVideo: 'movie',\n        subtitleOptions,\n        overview,\n        color: palette\n          ? Object.values(palette).sort((a, b) =>\n              a?.population === undefined || b?.population === undefined\n                ? 0\n                : b.population - a.population,\n            )[0]?.hex\n          : undefined,\n      },\n      {\n        headers: {\n          'Cache-Control': CACHE_CONTROL.watch,\n        },\n      },\n    );\n  }\n\n  if (provider === 'KissKh') {\n    if (!idProvider) throw new Response('Id Not Found', { status: 404 });\n    const [episodeDetail, imdbRating, fimg] = await Promise.all([\n      getKissKhInfo(Number(idProvider)),\n      detail?.imdb_id && process.env.IMDB_API_URL !== undefined\n        ? getImdbRating(detail?.imdb_id)\n        : undefined,\n      fetch(extractColorImageUrl),\n    ]);\n    const fimgb = Buffer.from(await fimg.arrayBuffer());\n    const [episodeStream, episodeSubtitle, palette] = await Promise.all([\n      getKissKhEpisodeStream(Number(episodeDetail?.episodes[0]?.id)),\n      episodeDetail?.episodes[0] && episodeDetail?.episodes[0].sub > 0\n        ? getKissKhEpisodeSubtitle(Number(episodeDetail?.episodes[0]?.id))\n        : undefined,\n      detail?.backdrop_path || detail?.poster_path\n        ? await Vibrant.from(fimgb).getPalette()\n        : undefined,\n    ]);\n\n    return json(\n      {\n        provider,\n        detail,\n        recommendations,\n        imdbRating,\n        sources: [{ url: episodeStream?.Video || '', isM3U8: true, quality: 'auto' }],\n        subtitles: episodeSubtitle?.map((sub) => ({\n          lang: sub.label,\n          url: sub.src,\n          ...(sub.default && { default: true }),\n        })),\n        userId: user?.id,\n        routePlayer,\n        titlePlayer,\n        id: mid,\n        posterPlayer,\n        typeVideo: 'movie',\n        subtitleOptions,\n        overview,\n        color: palette\n          ? Object.values(palette).sort((a, b) =>\n              a?.population === undefined || b?.population === undefined\n                ? 0\n                : b.population - a.population,\n            )[0]?.hex\n          : undefined,\n      },\n      {\n        headers: {\n          'Cache-Control': CACHE_CONTROL.watch,\n        },\n      },\n    );\n  }\n  const [imdbRating, fimg] = await Promise.all([\n    detail?.imdb_id && process.env.IMDB_API_URL !== undefined\n      ? getImdbRating(detail?.imdb_id)\n      : undefined,\n    fetch(extractColorImageUrl),\n  ]);\n  const fimgb = Buffer.from(await fimg.arrayBuffer());\n  const palette =\n    detail?.backdrop_path || detail?.poster_path\n      ? await Vibrant.from(fimgb).getPalette()\n      : undefined;\n  return json(\n    {\n      detail,\n      recommendations,\n      imdbRating,\n      userId: user?.id,\n      routePlayer,\n      titlePlayer,\n      id: mid,\n      posterPlayer,\n      typeVideo: 'movie',\n      subtitleOptions,\n      overview,\n      color: palette\n        ? Object.values(palette).sort((a, b) =>\n            a?.population === undefined || b?.population === undefined\n              ? 0\n              : b.population - a.population,\n          )[0]?.hex\n        : undefined,\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.watch,\n      },\n    },\n  );\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <>\n      <BreadcrumbItem\n        to={`/movies/${match.params.movieId}/`}\n        key={`movies-${match.params.movieId}-overview`}\n      >\n        {(match.data as { detail: IMovieDetail })?.detail?.title || match.params.movieId}\n      </BreadcrumbItem>\n      <BreadcrumbItem\n        to={`/movies/${match.params.movieId}/watch`}\n        key={`movies-${match.params.movieId}-watch`}\n      >\n        {t('watch')}\n      </BreadcrumbItem>\n    </>\n  ),\n  playerSettings: {\n    isMini: false,\n    shouldShowPlayer: true,\n  },\n  miniTitle: ({ match, t }) => ({\n    title: (match.data as { detail: IMovieDetail })?.detail?.title || '',\n    subtitle: t('watch'),\n    showImage: (match.data as { detail: IMovieDetail })?.detail?.poster_path !== undefined,\n    imageUrl: TMDB.posterUrl(\n      (match.data as { detail: IMovieDetail })?.detail?.poster_path || '',\n      'w92',\n    ),\n  }),\n};\n\nconst MovieWatch = () => {\n  const { detail, recommendations, imdbRating, color } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const id = detail && detail.id;\n  const releaseYear = new Date(detail?.release_date ?? '').getFullYear();\n  return (\n    <div className=\"mt-3 flex w-full flex-col items-center justify-center px-3 sm:px-0\">\n      <WatchDetail\n        id={Number(id)}\n        type=\"movie\"\n        title={`${detail?.title} (${releaseYear})`}\n        overview={detail?.overview || ''}\n        posterPath={detail?.poster_path ? TMDB.posterUrl(detail?.poster_path, 'w342') : undefined}\n        tmdbRating={detail?.vote_average}\n        imdbRating={imdbRating?.star}\n        genresMedia={detail?.genres}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        recommendationsMovies={recommendations?.items}\n        color={color}\n      />\n    </div>\n  );\n};\n\nexport function ErrorBoundary() {\n  return (\n    <ErrorBoundaryView\n      statusHandlers={{\n        404: ({ params }) => <p>There is no movie with the ID: {params.movieId}</p>,\n      }}\n    />\n  );\n}\n\nexport default MovieWatch;\n"
  },
  {
    "path": "app/routes/movies+/_index.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion } from 'framer-motion';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getListMovies, getTrending } from '~/services/tmdb/tmdb.server';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const page = 1;\n  const [trending, popular, topRated, upcoming, nowPlaying] = await Promise.all([\n    getTrending('movie', 'day', locale, page),\n    getListMovies('popular', locale, page),\n    getListMovies('top_rated', locale, page),\n    getListMovies('upcoming', locale, page),\n    getListMovies('now_playing', locale, page),\n  ]);\n  return json(\n    {\n      trending,\n      popular,\n      topRated,\n      upcoming,\n      nowPlaying,\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.movie,\n      },\n    },\n  );\n};\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Movies' },\n  { name: 'description', content: 'Discover movies in Sora' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/movies/' },\n  { property: 'og:title', content: 'Sora - Movies' },\n  { property: 'og:description', content: 'Discover movies in Sora' },\n  { name: 'twitter:title', content: 'Sora - Movies' },\n  { name: 'twitter:description', content: 'Discover movies in Sora' },\n]);\n\nexport const handle: Handle = {\n  disableLayoutPadding: true,\n  miniTitle: ({ t }) => ({\n    title: t('movies'),\n    showImage: false,\n  }),\n};\n\nconst MoviesIndexPage = () => {\n  const { trending, popular, topRated, upcoming, nowPlaying } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const location = useLocation();\n  const navigate = useNavigate();\n  const { t } = useTranslation();\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ x: '-10%', opacity: 0 }}\n      animate={{ x: '0', opacity: 1 }}\n      exit={{ y: '-10%', opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      style={{\n        width: '100%',\n        display: 'flex',\n        justifyContent: 'center',\n        flexDirection: 'column',\n        alignItems: 'center',\n      }}\n    >\n      <MediaList\n        listType=\"slider-banner\"\n        items={trending.items}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n      />\n      <div className=\"mt-9 flex w-full flex-col items-center justify-start px-3 sm:px-5\">\n        {popular?.items && popular?.items?.length > 0 ? (\n          <MediaList\n            genresMovie={rootData?.genresMovie}\n            genresTv={rootData?.genresTv}\n            items={popular.items}\n            itemsType=\"movie\"\n            listName={t('popular-movies')}\n            listType=\"slider-card\"\n            navigationButtons\n            onClickViewMore={() => navigate('/movies/popular')}\n            showMoreList\n          />\n        ) : null}\n        {topRated?.items && topRated?.items?.length > 0 ? (\n          <MediaList\n            genresMovie={rootData?.genresMovie}\n            genresTv={rootData?.genresTv}\n            items={topRated.items}\n            itemsType=\"movie\"\n            listName={t('top-rated-movies')}\n            listType=\"slider-card\"\n            navigationButtons\n            onClickViewMore={() => navigate('/movies/top-rated')}\n            showMoreList\n          />\n        ) : null}\n        {nowPlaying?.items && nowPlaying.items?.length > 0 ? (\n          <MediaList\n            genresMovie={rootData?.genresMovie}\n            genresTv={rootData?.genresTv}\n            items={nowPlaying.items}\n            itemsType=\"movie\"\n            listName={t('now-playing-movies')}\n            listType=\"slider-card\"\n            navigationButtons\n            onClickViewMore={() => navigate('/movies/now-playing')}\n            showMoreList\n          />\n        ) : null}\n        {upcoming?.items && upcoming.items?.length > 0 ? (\n          <MediaList\n            genresMovie={rootData?.genresMovie}\n            genresTv={rootData?.genresTv}\n            items={upcoming.items}\n            itemsType=\"movie\"\n            listName={t('upcoming-movies')}\n            listType=\"slider-card\"\n            navigationButtons\n            onClickViewMore={() => navigate('/movies/upcoming')}\n            showMoreList\n          />\n        ) : null}\n      </div>\n    </motion.div>\n  );\n};\n\nexport default MoviesIndexPage;\n"
  },
  {
    "path": "app/routes/movies+/now-playing.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getListMovies } from '~/services/tmdb/tmdb.server';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Now Playing Movies' },\n  { name: 'description', content: 'Now Playing Movies' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/movies/now-playing' },\n  { property: 'og:title', content: 'Sora - Now Playing Movies' },\n  { property: 'og:description', content: 'Now Playing Movies' },\n  { name: 'twitter:title', content: 'Sora - Now Playing Movies' },\n  { name: 'twitter:description', content: 'Now Playing Movies' },\n]);\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page'));\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  return json(\n    {\n      movies: await getListMovies('now_playing', locale, page),\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.nowPlaying,\n      },\n    },\n  );\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/movies/now-playing\" key={`movies-now-playing`}>\n      {t('now-playing-movies')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('movies'),\n    subtitle: t('now-playing'),\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst ListNowPlayingMovies = () => {\n  const { movies } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const location = useLocation();\n  const navigate = useNavigate();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_e: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate('/movies/popular');\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate('/movies/upcoming');\n    }\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <MediaList\n        currentPage={movies?.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={movies?.items}\n        itemsType=\"movie\"\n        listName={t('now-playing-movies')}\n        listType=\"grid\"\n        showListTypeChangeButton\n        totalPages={movies?.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default ListNowPlayingMovies;\n"
  },
  {
    "path": "app/routes/movies+/popular.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getListMovies } from '~/services/tmdb/tmdb.server';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Popular Movies' },\n  { name: 'description', content: 'Popular Movies' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/movies/popular' },\n  { property: 'og:title', content: 'Sora - Popular Movies' },\n  { property: 'og:description', content: 'Popular Movies' },\n  { name: 'twitter:title', content: 'Sora - Popular Movies' },\n  { name: 'twitter:description', content: 'Popular Movies' },\n]);\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page'));\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  return json(\n    {\n      movies: await getListMovies('popular', locale, page),\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.popular,\n      },\n    },\n  );\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/movies/popular\" key=\"movies-popular\">\n      {t('popular-movies')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('movies'),\n    subtitle: t('popular'),\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst ListPopularMovies = () => {\n  const { movies } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const location = useLocation();\n  const navigate = useNavigate();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_e: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      return;\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate('/movies/now-playing');\n    }\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <MediaList\n        currentPage={movies?.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={movies?.items}\n        itemsType=\"movie\"\n        listName={t('popular-movies')}\n        listType=\"grid\"\n        showListTypeChangeButton\n        totalPages={movies?.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default ListPopularMovies;\n"
  },
  {
    "path": "app/routes/movies+/top-rated.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getListMovies } from '~/services/tmdb/tmdb.server';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Top Rated Movies' },\n  { name: 'description', content: 'Top Rated Movies' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/movies/top-rated' },\n  { property: 'og:title', content: 'Sora - Top Rated Movies' },\n  { property: 'og:description', content: 'Top Rated Movies' },\n  { name: 'twitter:title', content: 'Sora - Top Rated Movies' },\n  { name: 'twitter:description', content: 'Top Rated Movies' },\n]);\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page'));\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  return json(\n    {\n      movies: await getListMovies('top_rated', locale, page),\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.topRated,\n      },\n    },\n  );\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/movies/top-rated\" key=\"movies-top-rated\">\n      {t('top-rated-movies')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('movies'),\n    subtitle: t('top-rated'),\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst ListTopRatedMovies = () => {\n  const { movies } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const location = useLocation();\n  const navigate = useNavigate();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate('/movies/upcoming');\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      return;\n    }\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <MediaList\n        currentPage={movies?.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={movies?.items}\n        itemsType=\"movie\"\n        listName={t('top-rated-movies')}\n        listType=\"grid\"\n        showListTypeChangeButton\n        totalPages={movies?.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default ListTopRatedMovies;\n"
  },
  {
    "path": "app/routes/movies+/upcoming.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getListMovies } from '~/services/tmdb/tmdb.server';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Upcoming Movies' },\n  { name: 'description', content: 'Upcoming Movies' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/movies/upcoming' },\n  { property: 'og:title', content: 'Sora - Upcoming Movies' },\n  { property: 'og:description', content: 'Upcoming Movies' },\n  { name: 'twitter:title', content: 'Sora - Upcoming Movies' },\n  { name: 'twitter:description', content: 'Upcoming Movies' },\n]);\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page'));\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  return json(\n    {\n      movies: await getListMovies('upcoming', locale, page),\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.trending,\n      },\n    },\n  );\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/movies/upcoming\" key=\"movies-upcoming\">\n      {t('upcoming-movies')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('movies'),\n    subtitle: t('upcoming'),\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst ListUpcomingMovies = () => {\n  const { movies } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const location = useLocation();\n  const navigate = useNavigate();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate('/movies/now-playing');\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate('/movies/top-rated');\n    }\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <MediaList\n        currentPage={movies?.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={movies?.items}\n        itemsType=\"movie\"\n        listName={t('upcoming-movies')}\n        listType=\"grid\"\n        showListTypeChangeButton\n        totalPages={movies?.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default ListUpcomingMovies;\n"
  },
  {
    "path": "app/routes/movies.tsx",
    "content": "import { Outlet } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport { moviePages } from '~/constants/tabLinks';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  {\n    name: 'keywords',\n    content:\n      'watch free movies, free movies to watch online, watch movies online free, free movies streaming, free movies full, free movies download, watch movies hd, movies to watch, hd movies, stream movies, movies to stream, watch movies free',\n  },\n  { property: 'og:image', content: 'https://sorachill.vercel.app/api/ogimage?it=movies' },\n  { name: 'twitter:image', content: 'https://sorachill.vercel.app/api/ogimage?it=movies' },\n]);\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/movies\" key=\"movies\">\n      {t('movies')}\n    </BreadcrumbItem>\n  ),\n  showTabLink: true,\n  tabLinkPages: moviePages,\n  tabLinkTo: () => '/movies',\n  hideTabLinkWithLocation: (locationPathname: string) => {\n    if (locationPathname.split('/')[2]?.match(/^\\d+$/) || locationPathname === '/movies')\n      return true;\n    return false;\n  },\n};\n\nconst MoviePage = () => <Outlet />;\n\nexport default MoviePage;\n"
  },
  {
    "path": "app/routes/people+/$peopleId+/_index.tsx",
    "content": "import { useEffect, useState } from 'react';\nimport { Spacer } from '@nextui-org/spacer';\nimport { useFetcher } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { useTranslation } from 'react-i18next';\n\nimport type { IMedia } from '~/types/media';\nimport type { loader as peopleIdLoader } from '~/routes/people+/$peopleId';\nimport type { IMediaList } from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport MediaList from '~/components/media/MediaList';\n\nexport const meta = mergeMeta<null, { 'routes/people+/$peopleId': typeof peopleIdLoader }>(\n  ({ params, matches }) => {\n    const peopleData = matches.find((match) => match.id === 'routes/people+/$peopleId')?.data;\n    if (!peopleData) {\n      return [\n        { title: 'Missing People' },\n        { name: 'description', content: `There is no people with the ID: ${params.peopleId}` },\n      ];\n    }\n    const { detail } = peopleData;\n    const { name } = detail || {};\n    const peopleTitle = name || '';\n    return [\n      { title: `Sora - ${peopleTitle}` },\n      {\n        property: 'og:url',\n        content: `https://sorachill.vercel.app/people/${params.peopleId}/`,\n      },\n      { property: 'og:title', content: `Sora - ${peopleTitle}` },\n      { name: 'twitter:title', content: `Sora - ${peopleTitle}` },\n    ];\n  },\n);\n\nconst OverviewPage = () => {\n  const peopleData = useTypedRouteLoaderData('routes/people+/$peopleId');\n  const rootData = useTypedRouteLoaderData('root');\n  const fetcher = useFetcher<{ searchResults: IMediaList }>();\n  const { t } = useTranslation();\n  const [knownFor, setKnownFor] = useState<IMedia[]>();\n  useEffect(() => {\n    if (peopleData?.detail?.name) {\n      fetcher.load(`/search/people/${peopleData?.detail?.name}`);\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n  useEffect(() => {\n    if (fetcher.data && fetcher.data.searchResults) {\n      const { items } = fetcher.data.searchResults;\n      if (items) {\n        const peopleFound = items.find((result: IMedia) => result.id === peopleData?.detail?.id);\n        if (peopleFound) {\n          setKnownFor(TMDB.postFetchDataHandler(peopleFound?.knownFor));\n        }\n      }\n    }\n  }, [fetcher.data, peopleData?.detail?.id]);\n  return (\n    <>\n      {peopleData?.detail?.biography ? (\n        <>\n          <div className=\"flex flex-col gap-y-2\">\n            <h4>{t('biography')}</h4>\n            <p style={{ whiteSpace: 'pre-line', textAlign: 'justify' }}>\n              {/* TODO: add a read more button */}\n              {peopleData?.detail?.biography}\n            </p>\n          </div>\n          <Spacer y={7} />\n        </>\n      ) : null}\n      {knownFor && knownFor.length > 0 ? (\n        <>\n          <h4>{t('known-for')}</h4>\n          <Spacer y={2.5} />\n          <MediaList\n            genresMovie={rootData?.genresMovie}\n            genresTv={rootData?.genresTv}\n            items={knownFor}\n            itemsType=\"movie-tv\"\n            listType=\"slider-card\"\n          />\n        </>\n      ) : null}\n    </>\n  );\n};\n\nexport default OverviewPage;\n"
  },
  {
    "path": "app/routes/people+/$peopleId+/credits.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion } from 'framer-motion';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as peopleIdLoader } from '~/routes/people+/$peopleId';\nimport { i18next } from '~/services/i18n';\nimport { getPeopleCredits } from '~/services/tmdb/tmdb.server';\nimport type { IPeopleDetail } from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const locale = await i18next.getLocale(request);\n  const { peopleId } = params;\n  const pid = Number(peopleId);\n  if (!pid) throw new Response('Not Found', { status: 404 });\n\n  return json(\n    {\n      credits: await getPeopleCredits(pid, undefined, locale),\n    },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.detail },\n    },\n  );\n};\n\nexport const meta = mergeMeta<null, { 'routes/people+/$peopleId': typeof peopleIdLoader }>(\n  ({ params, matches }) => {\n    const peopleData = matches.find((match) => match.id === 'routes/people+/$peopleId')?.data;\n    if (!peopleData) {\n      return [\n        { title: 'Missing People' },\n        { name: 'description', content: `There is no people with the ID: ${params.peopleId}` },\n      ];\n    }\n    const { detail } = peopleData;\n    const { name } = detail || {};\n    const peopleTitle = name || '';\n    return [\n      { title: `Sora - ${peopleTitle} - Credits` },\n      {\n        property: 'og:url',\n        content: `https://sorachill.vercel.app/people/${params.peopleId}/credits`,\n      },\n      { property: 'og:title', content: `Sora - ${peopleTitle} - Credits` },\n      { name: 'twitter:title', content: `Sora - ${peopleTitle} - Credits` },\n    ];\n  },\n);\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/people/${match.params.peopleId}/credits`}\n      key={`people-${match.params.peopleId}-credits`}\n    >\n      {t('credits')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch\n      ? (parentMatch?.data as { detail: IPeopleDetail })?.detail?.name || 'People'\n      : 'People',\n    subtitle: t('credits'),\n    showImage: parentMatch\n      ? (parentMatch?.data as { detail: IPeopleDetail })?.detail?.profile_path !== undefined\n      : false,\n    imageUrl: parentMatch\n      ? TMDB.profileUrl(\n          (parentMatch?.data as { detail: IPeopleDetail })?.detail?.profile_path,\n          'w45',\n        )\n      : undefined,\n  }),\n};\n\nconst CreditsPage = () => {\n  const { credits } = useLoaderData<typeof loader>();\n  const location = useLocation();\n  const rootData = useTypedRouteLoaderData('root');\n  // TODO: Add filter and sort data\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n    >\n      <MediaList\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        isCreditsCard\n        items={credits?.cast}\n        itemsType=\"movie-tv\"\n        listType=\"grid\"\n      />\n    </motion.div>\n  );\n};\n\nexport default CreditsPage;\n"
  },
  {
    "path": "app/routes/people+/$peopleId+/media.tsx",
    "content": "import * as React from 'react';\nimport { Spacer } from '@nextui-org/spacer';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { useTranslation } from 'react-i18next';\nimport { Gallery, Item, type GalleryProps } from 'react-photoswipe-gallery';\nimport { MimeType } from 'remix-image';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as peopleIdLoader } from '~/routes/people+/$peopleId';\nimport { i18next } from '~/services/i18n';\nimport { getPeopleImages } from '~/services/tmdb/tmdb.server';\nimport type { IPeopleDetail } from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport Image from '~/components/elements/Image';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const locale = await i18next.getLocale(request);\n  const { peopleId } = params;\n  const pid = Number(peopleId);\n  if (!pid) throw new Response('Not Found', { status: 404 });\n\n  const images = await getPeopleImages(pid, locale);\n  if (!images) throw new Response('Not Found', { status: 404 });\n\n  return json(\n    { images },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.detail },\n    },\n  );\n};\n\nexport const meta = mergeMeta<null, { 'routes/people+/$peopleId': typeof peopleIdLoader }>(\n  ({ params, matches }) => {\n    const peopleData = matches.find((match) => match.id === 'routes/people+/$peopleId')?.data;\n    if (!peopleData) {\n      return [\n        { title: 'Missing People' },\n        { name: 'description', content: `There is no people with the ID: ${params.peopleId}` },\n      ];\n    }\n    const { detail } = peopleData;\n    const { name } = detail || {};\n    const peopleTitle = name || '';\n    return [\n      { title: `Sora - ${peopleTitle} - Media` },\n      {\n        property: 'og:url',\n        content: `https://sorachill.vercel.app/people/${params.peopleId}/media`,\n      },\n      { property: 'og:title', content: `Sora - ${peopleTitle} - Media` },\n      { name: 'twitter:title', content: `Sora - ${peopleTitle} - Media` },\n    ];\n  },\n);\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/people/${match.params.peopleId}/media`}\n      key={`people-${match.params.peopleId}-media`}\n    >\n      {t('media')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch\n      ? (parentMatch?.data as { detail: IPeopleDetail })?.detail?.name || 'People'\n      : 'People',\n    subtitle: t('media'),\n    showImage: parentMatch\n      ? (parentMatch?.data as { detail: IPeopleDetail })?.detail?.profile_path !== undefined\n      : false,\n    imageUrl: parentMatch\n      ? TMDB.profileUrl(\n          (parentMatch?.data as { detail: IPeopleDetail })?.detail?.profile_path,\n          'w45',\n        )\n      : undefined,\n  }),\n};\n\nconst MediaPage = () => {\n  const { images } = useLoaderData<typeof loader>();\n  const { t } = useTranslation();\n  const peopleData = useTypedRouteLoaderData('routes/people+/$peopleId');\n\n  const uiElements: GalleryProps['uiElements'] = [\n    {\n      name: 'custom-rotate-button',\n      ariaLabel: 'Rotate',\n      order: 9,\n      isButton: true,\n      html: {\n        isCustomSVG: true,\n        inner:\n          '<path d=\"M13.887 6.078C14.258 6.234 14.5 6.598 14.5 7V8.517C18.332 8.657 21.258 10.055 23.15 12.367 24.519 14.041 25.289 16.13 25.496 18.409A1 1 0 0123.504 18.591C23.327 16.645 22.68 14.952 21.601 13.633 20.156 11.867 17.831 10.653 14.5 10.517V12A1.002 1.002 0 0112.779 12.693L10.304 10.121A1.002 1.002 0 0110.324 8.713L12.8 6.286A1 1 0 0113.887 6.078ZM7.5 16A1.5 1.5 0 006 17.5V24.5A1.5 1.5 0 007.5 26H17.5A1.5 1.5 0 0019 24.5V17.5A1.5 1.5 0 0017.5 16H7.5Z\" id=\"pswp__icn-rotate\"/>',\n        outlineID: 'pswp__icn-rotate',\n      },\n      appendTo: 'bar',\n      onClick: (_, __, pswpInstance) => {\n        const item = pswpInstance.currSlide?.content.element;\n\n        const prevRotateAngle = Number(item?.dataset.rotateAngel) || 0;\n        const rotateAngle = prevRotateAngle === 270 ? 0 : prevRotateAngle + 90;\n\n        // add slide rotation\n        if (item) {\n          item.style.transform = `${item.style.transform?.replace(\n            `rotate(-${prevRotateAngle}deg)`,\n            '',\n          )} rotate(-${rotateAngle}deg)`;\n          item.dataset.rotateAngel = String(rotateAngle);\n        }\n      },\n      onInit: (_, pswpInstance) => {\n        // remove applied rotation on slide change\n        // https://photoswipe.com/events/#slide-content-events\n        pswpInstance.on('contentRemove', () => {\n          const item = pswpInstance.currSlide?.content.element;\n          if (item) {\n            item.style.transform = `${item.style.transform?.replace(\n              `rotate(-${item.dataset.rotateAngel}deg)`,\n              '',\n            )}`;\n            delete item.dataset.rotateAngel;\n          }\n        });\n      },\n    },\n  ];\n  return (\n    <>\n      <h5 className=\"w-full\">\n        <strong>{t('profiles')}</strong>\n      </h5>\n      <Spacer y={2.5} />\n      <Gallery withCaption withDownloadButton uiElements={uiElements}>\n        <div className=\"grid grid-cols-1 gap-3 xs:grid-cols-2 xl:grid-cols-3 3xl:grid-cols-4 5xl:grid-cols-5\">\n          {images?.profiles?.map((image) => (\n            <Item\n              key={image.file_path}\n              cropped\n              original={TMDB.profileUrl(image?.file_path, 'original')}\n              thumbnail={TMDB.profileUrl(image?.file_path, 'w185')}\n              alt={`Photo of ${peopleData?.detail?.name} size ${image.width}x${image.height}`}\n              caption={`Photo of ${peopleData?.detail?.name} size ${image.width}x${image.height}`}\n              width={image.width}\n              height={image.height}\n            >\n              {({ ref, open }) => (\n                <Image\n                  src={TMDB.profileUrl(image?.file_path, 'w185')}\n                  ref={ref as React.MutableRefObject<HTMLImageElement>}\n                  onClick={open}\n                  alt={`Photo of ${peopleData?.detail?.name} image size ${image.width}x${image.height}`}\n                  radius=\"lg\"\n                  classNames={{\n                    img: 'h-auto min-w-[120px] cursor-pointer object-cover 2xs:min-w-[185px]',\n                  }}\n                  loading=\"lazy\"\n                  title={peopleData?.detail?.name}\n                  placeholder=\"empty\"\n                  options={{\n                    contentType: MimeType.WEBP,\n                  }}\n                />\n              )}\n            </Item>\n          ))}\n        </div>\n      </Gallery>\n    </>\n  );\n};\n\nexport default MediaPage;\n"
  },
  {
    "path": "app/routes/people+/$peopleId.tsx",
    "content": "import { Spacer } from '@nextui-org/spacer';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { Outlet, useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getPeopleDetail, getPeopleExternalIds } from '~/services/tmdb/tmdb.server';\nimport type { IPeopleDetail } from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport { peopleDetailPages } from '~/constants/tabLinks';\nimport PeopleDetail from '~/components/media/PeopleDetail';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport ErrorBoundaryView from '~/components/elements/shared/ErrorBoundaryView';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const { peopleId } = params;\n  const pid = Number(peopleId);\n  if (!pid) throw new Response('Not Found', { status: 404 });\n\n  const detail = await getPeopleDetail(pid, locale);\n  const externalIds = await getPeopleExternalIds(pid, locale);\n  if (!detail || !externalIds) throw new Response('Not Found', { status: 404 });\n\n  return json(\n    {\n      detail,\n      externalIds: {\n        facebookId: externalIds.facebook_id || null,\n        instagramId: externalIds.instagram_id || null,\n        twitterId: externalIds.twitter_id || null,\n      },\n    },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.detail },\n    },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader>(({ data, params }) => {\n  if (!data) {\n    return [\n      { title: 'Missing People' },\n      { name: 'description', content: `There is no people with the ID: ${params.peopleId}` },\n    ];\n  }\n  const { detail } = data;\n  const { name, biography } = detail || {};\n  const peopleTitle = name || '';\n  return [\n    { name: 'description', content: biography },\n    {\n      name: 'keywords',\n      content: `watch ${peopleTitle} free, watch ${peopleTitle} movies, watch ${peopleTitle} series, stream ${peopleTitle} series, ${peopleTitle} movies online free`,\n    },\n    { property: 'og:description', content: biography },\n    {\n      property: 'og:image',\n      content: detail?.profile_path ? TMDB.profileUrl(detail?.profile_path, 'w185') : undefined,\n    },\n    { name: 'twitter:description', content: biography },\n    {\n      name: 'twitter:image',\n      content: detail?.profile_path ? TMDB.profileUrl(detail?.profile_path, 'w185') : undefined,\n    },\n  ];\n});\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <>\n      <BreadcrumbItem to=\"/people\" key=\"people\">\n        {t('popular-people')}\n      </BreadcrumbItem>\n      <BreadcrumbItem\n        to={`/people/${match.params.peopleId}`}\n        key={`people-${match.params.peopleId}`}\n      >\n        {(match.data as { detail: IPeopleDetail })?.detail?.name || match.params.peopleId}\n      </BreadcrumbItem>\n    </>\n  ),\n  showTabLink: true,\n  tabLinkPages: peopleDetailPages,\n  tabLinkTo: ({ params }) => `/people/${params.peopleId}`,\n  miniTitle: ({ match, t }) => ({\n    title: (match.data as { detail: IPeopleDetail })?.detail?.name || t('people'),\n    subtitle: t('overview'),\n    showImage: (match.data as { detail: IPeopleDetail })?.detail?.profile_path !== undefined,\n    imageUrl: TMDB.profileUrl(\n      (match.data as { detail: IPeopleDetail })?.detail?.profile_path,\n      'w45',\n    ),\n  }),\n};\n\nconst PeopleDetailPage = () => {\n  const { detail, externalIds } = useLoaderData<typeof loader>();\n  return (\n    <div className=\"mt-9 flex w-full flex-row flex-wrap items-stretch justify-center px-3 sm:px-5\">\n      <div className=\"w-full sm:w-1/3\">\n        <PeopleDetail detail={detail} externalIds={externalIds} />\n        <Spacer y={5} />\n      </div>\n      <div className=\"w-full sm:w-2/3\">\n        <Outlet />\n      </div>\n    </div>\n  );\n};\n\nexport function ErrorBoundary() {\n  return (\n    <ErrorBoundaryView\n      statusHandlers={{\n        404: ({ params }) => <p>There is no people with the ID: {params.peopleId}</p>,\n      }}\n    />\n  );\n}\n\nexport default PeopleDetailPage;\n"
  },
  {
    "path": "app/routes/people+/_index.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion } from 'framer-motion';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getListPeople } from '~/services/tmdb/tmdb.server';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Popular People' },\n  { name: 'description', content: 'Discover the most popular celebrities on Sora.' },\n  {\n    name: 'keywords',\n    content:\n      'popular celebrities, popular celebrity, top celebrities, top celebrity, people celebrity, celebrity people, best celebrity, best celebrities, famous celebrity, famous people, celebrity movies, movies by celebrity, celebrity tv shows, tv show celebrities, celebrity television shows, celebrity tv series',\n  },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/people' },\n  { property: 'og:title', content: 'Sora - Popular People' },\n  { property: 'og:image', content: 'https://sorachill.vercel.app/api/ogimage?it=people' },\n  { property: 'og:description', content: 'Discover the most popular celebrities on Sora.' },\n  { name: 'twitter:title', content: 'Sora - Popular People' },\n  { name: 'twitter:description', content: 'Discover the most popular celebrities on Sora.' },\n  { name: 'twitter:image', content: 'https://sorachill.vercel.app/api/ogimage?it=people' },\n]);\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page')) || undefined;\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  return json(\n    {\n      people: await getListPeople('popular', locale, page),\n    },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.trending },\n    },\n  );\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/people\" key=\"people\">\n      {t('popular-people')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('people'),\n    subtitle: t('popular'),\n    showImage: false,\n  }),\n};\n\nconst ListPeoplePopular = () => {\n  const { people } = useLoaderData<typeof loader>();\n  const location = useLocation();\n  const { t } = useTranslation();\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ x: '-10%', opacity: 0 }}\n      animate={{ x: '0', opacity: 1 }}\n      exit={{ y: '-10%', opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n    >\n      <MediaList\n        currentPage={people?.page}\n        items={people?.items}\n        itemsType=\"people\"\n        listName={t('popular-people')}\n        listType=\"grid\"\n        totalPages={people?.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default ListPeoplePopular;\n"
  },
  {
    "path": "app/routes/robots[.txt].tsx",
    "content": "import { generateRobotsTxt } from '@nasa-gcn/remix-seo';\n\nexport function loader() {\n  return generateRobotsTxt(\n    [\n      { type: 'userAgent', value: 'Bingbot' },\n      { type: 'allow', value: '/$' },\n      { type: 'allow', value: '/movies$' },\n      { type: 'allow', value: '/tv-shows$' },\n      { type: 'allow', value: '/anime$' },\n      { type: 'allow', value: '/trending$' },\n      { type: 'allow', value: '/sign-in$' },\n      { type: 'allow', value: '/sign-up$' },\n      { type: 'disallow', value: '/' },\n      { type: 'crawlDelay', value: '10' },\n      { type: 'userAgent', value: 'SemrushBot' },\n      { type: 'disallow', value: '/' },\n      { type: 'userAgent', value: '*' },\n      { type: 'disallow', value: '' },\n      { type: 'sitemap', value: 'https://sorachill.vercel.app/sitemap.xml' },\n    ],\n    {\n      appendOnDefaultPolicies: false,\n      headers: {\n        'Cache-Control': `public, max-age=${60 * 5}`,\n      },\n    },\n  );\n}\n"
  },
  {
    "path": "app/routes/search+/_index.tsx",
    "content": "const SearchIndexPage = () => <h4> Choose a search.</h4>;\n\nexport default SearchIndexPage;\n"
  },
  {
    "path": "app/routes/search+/anime+/$animeKeyword.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport type { IMedia } from '~/types/media';\nimport { getAnimeSearch } from '~/services/consumet/anilist/anilist.server';\nimport { authenticate } from '~/services/supabase';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport SearchForm from '~/components/elements/SearchForm';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const keyword = params?.animeKeyword || '';\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page'));\n  if (!page && (page < 1 || page > 1000)) page = 1;\n\n  return json(\n    {\n      searchResults: await getAnimeSearch(keyword, page, 20),\n    },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.search },\n    },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader>(({ data, params }) => {\n  // @ts-expect-error\n  const { searchResults } = data;\n  return [\n    { title: `Sora - Search results for ${params.animeKeyword}` },\n    {\n      name: 'keywords',\n      content: `Watch ${params.animeKeyword}, Stream ${params.animeKeyword}, Watch ${params.animeKeyword} HD, Online ${params.animeKeyword}, Streaming ${params.animeKeyword}, English, Subtitle ${params.animeKeyword}, English Subtitle`,\n    },\n    {\n      property: 'og:url',\n      content: `https://sorachill.vercel.app/search/anime/${params.animeKeyword}`,\n    },\n    { property: 'og:title', content: `Sora - Search results for ${params.animeKeyword}` },\n    {\n      property: 'og:image',\n      content: searchResults?.results[0]?.cover || searchResults?.results[0]?.image || '',\n    },\n    {\n      name: 'twitter:image',\n      content: searchResults?.results[0]?.cover || searchResults?.results[0]?.image || '',\n    },\n    { name: 'twitter:title', content: `Sora - Search results for ${params.animeKeyword}` },\n  ];\n});\n\nexport const handle: Handle = {\n  breadcrumb: ({ match }) => (\n    <BreadcrumbItem\n      to={`/search/anime/${match.params.animeKeyword}`}\n      key={`search-anime-${match.params.animeKeyword}`}\n    >\n      {match.params.animeKeyword}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ match }) => ({\n    title: 'Search results',\n    subtitle: match.params.animeKeyword,\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst SearchRoute = () => {\n  const { searchResults } = useLoaderData<typeof loader>();\n  const navigate = useNavigate();\n  const { t } = useTranslation();\n  const location = useLocation();\n  const isHydrated = useHydrated();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate('/search/tv');\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate('/search/people');\n    }\n  };\n\n  const onSubmit = (value: string) => {\n    navigate(`/search/anime/${value}`);\n  };\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <SearchForm\n        onSubmit={onSubmit}\n        textOnButton={t('search.action')}\n        textHelper={t('search.helper.anime')}\n        textPlaceHolder={t('search.placeHolder.anime')}\n      />\n      <MediaList\n        currentPage={searchResults?.currentPage || 1}\n        hasNextPage={searchResults?.hasNextPage || false}\n        items={searchResults?.results as IMedia[]}\n        itemsType=\"anime\"\n        listName={t('search.searchResults')}\n        listType=\"grid\"\n        showListTypeChangeButton\n      />\n    </motion.div>\n  );\n};\n\nexport default SearchRoute;\n"
  },
  {
    "path": "app/routes/search+/anime+/_index.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport type { IMedia } from '~/types/media';\nimport { getAnimeTrending } from '~/services/consumet/anilist/anilist.server';\nimport { authenticate } from '~/services/supabase';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport SearchForm from '~/components/elements/SearchForm';\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page'));\n  if (!page && (page < 1 || page > 1000)) page = 1;\n\n  return json(\n    {\n      items: await getAnimeTrending(page, 20),\n    },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.trending },\n    },\n  );\n};\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Search Anime' },\n  { name: 'description', content: 'Search Anime on Sora' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/search/anime/' },\n  { property: 'og:title', content: 'Sora - Search Anime' },\n  { property: 'og:description', content: 'Search Anime on Sora' },\n  { name: 'twitter:title', content: 'Sora - Search Anime' },\n  { name: 'twitter:description', content: 'Search Anime on Sora' },\n]);\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/search/anime\" aria-label=\"Search Anime\">\n      Search Anime\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Search',\n    subtitle: 'Anime',\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst SearchRoute = () => {\n  const { items } = useLoaderData<typeof loader>();\n  const navigate = useNavigate();\n  const { t } = useTranslation();\n  const location = useLocation();\n  const isHydrated = useHydrated();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate('/search/tv');\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate('/search/people');\n    }\n  };\n\n  const onSubmit = (value: string) => {\n    navigate(`/search/anime/${value}`);\n  };\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <SearchForm\n        onSubmit={onSubmit}\n        textOnButton={t('search.action')}\n        textHelper={t('search.helper.anime')}\n        textPlaceHolder={t('search.placeHolder.anime')}\n      />\n      <MediaList\n        currentPage={items?.currentPage || 1}\n        hasNextPage={items?.hasNextPage || false}\n        items={items?.results as IMedia[]}\n        itemsType=\"anime\"\n        listName=\"Trending Anime\"\n        listType=\"grid\"\n        showListTypeChangeButton\n      />\n    </motion.div>\n  );\n};\n\nexport default SearchRoute;\n"
  },
  {
    "path": "app/routes/search+/movie+/$movieKeyword.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getSearchMovies } from '~/services/tmdb/tmdb.server';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport SearchForm from '~/components/elements/SearchForm';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const keyword = params?.movieKeyword || '';\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page')) || undefined;\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  return json(\n    {\n      searchResults: await getSearchMovies(keyword, page, locale),\n    },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.search },\n    },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader>(({ data, params }) => {\n  // @ts-expect-error\n  const { searchResults } = data;\n  return [\n    { title: `Sora - Search results for ${params.movieKeyword}` },\n    {\n      name: 'keywords',\n      content: `Watch ${params.movieKeyword}, Stream ${params.movieKeyword}, Watch ${params.movieKeyword} HD, Online ${params.movieKeyword}, Streaming ${params.movieKeyword}, English, Subtitle ${params.movieKeyword}, English Subtitle`,\n    },\n    {\n      property: 'og:url',\n      content: `https://sorachill.vercel.app/search/movie/${params.movieKeyword}`,\n    },\n    { property: 'og:title', content: `Sora - Search results for ${params.movieKeyword}` },\n    {\n      property: 'og:image',\n      content: searchResults?.items[0]?.backdropPath || searchResults?.items[0]?.posterPath || '',\n    },\n    {\n      name: 'twitter:image',\n      content: searchResults?.items[0]?.backdropPath || searchResults?.items[0]?.posterPath || '',\n    },\n    { name: 'twitter:title', content: `Sora - Search results for ${params.movieKeyword}` },\n  ];\n});\n\nexport const handle: Handle = {\n  breadcrumb: ({ match }) => (\n    <BreadcrumbItem\n      to={`/search/movie/${match.params.movieKeyword}`}\n      aria-label={match.params.movieKeyword}\n    >\n      {match.params.movieKeyword}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ match }) => ({\n    title: 'Search results',\n    subtitle: match.params.movieKeyword,\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst SearchRoute = () => {\n  const { searchResults } = useLoaderData<typeof loader>() || {};\n  const rootData = useTypedRouteLoaderData('root');\n  const navigate = useNavigate();\n  const location = useLocation();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      return;\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate('/search/tv');\n    }\n  };\n\n  const onSubmit = (value: string) => {\n    navigate(`/search/movie/${value}`);\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <SearchForm\n        onSubmit={onSubmit}\n        textHelper={t('search.helper.movie')}\n        textOnButton={t('search.action')}\n        textPlaceHolder={t('search.placeHolder.movie')}\n      />\n      <MediaList\n        currentPage={searchResults?.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={searchResults?.items}\n        itemsType=\"movie\"\n        listName={t('search.searchResults')}\n        listType=\"grid\"\n        showListTypeChangeButton\n        totalPages={searchResults?.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default SearchRoute;\n"
  },
  {
    "path": "app/routes/search+/movie+/_index.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getTrending } from '~/services/tmdb/tmdb.server';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport SearchForm from '~/components/elements/SearchForm';\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page')) || undefined;\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  return json(\n    {\n      todayTrending: await getTrending('all', 'day', locale, page),\n    },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.trending },\n    },\n  );\n};\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Search Movies' },\n  { name: 'description', content: 'Search Movies on Sora' },\n  {\n    property: 'og:url',\n    content: 'https://sorachill.vercel.app/search/movie/ ',\n  },\n  { property: 'og:title', content: 'Sora - Search Movies' },\n  { property: 'og:description', content: 'Search Movies on Sora' },\n  { name: 'twitter:title', content: 'Sora - Search Movies' },\n  { name: 'twitter:description', content: 'Search Movies on Sora' },\n]);\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/search/movie\" key=\"search-movies\">\n      Search Movies\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Search',\n    subtitle: 'Movies',\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst SearchRoute = () => {\n  const { todayTrending } = useLoaderData<typeof loader>() || {};\n  const rootData = useTypedRouteLoaderData('root');\n  const navigate = useNavigate();\n  const location = useLocation();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      return;\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate('/search/tv');\n    }\n  };\n\n  const onSubmit = (value: string) => {\n    navigate(`/search/movie/${value}`);\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <SearchForm\n        onSubmit={onSubmit}\n        textHelper={t('search.helper.movie')}\n        textOnButton={t('search.action')}\n        textPlaceHolder={t('search.placeHolder.movie')}\n      />\n      <MediaList\n        currentPage={todayTrending?.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={todayTrending?.items}\n        itemsType=\"movie-tv\"\n        listName={t('today-trending')}\n        listType=\"grid\"\n        showListTypeChangeButton\n        totalPages={todayTrending?.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default SearchRoute;\n"
  },
  {
    "path": "app/routes/search+/people+/$peopleKeyword.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getSearchPerson } from '~/services/tmdb/tmdb.server';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport SearchForm from '~/components/elements/SearchForm';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const keyword = params?.peopleKeyword || '';\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page')) || undefined;\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  return json(\n    {\n      searchResults: await getSearchPerson(keyword, page, undefined, locale),\n    },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.search },\n    },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader>(({ data, params }) => {\n  // @ts-expect-error\n  const { searchResults } = data;\n  return [\n    { title: `Sora - Search results for ${params.peopleKeyword}` },\n    {\n      name: 'keywords',\n      content: `${params.peopleKeyword}`,\n    },\n    {\n      property: 'og:url',\n      content: `https://sorachill.vercel.app/search/people/${params.peopleKeyword}`,\n    },\n    { property: 'og:title', content: `Sora - Search results for ${params.peopleKeyword}` },\n    {\n      property: 'og:image',\n      content: searchResults?.items[0]?.posterPath || '',\n    },\n    {\n      name: 'twitter:image',\n      content: searchResults?.items[0]?.posterPath || '',\n    },\n    { name: 'twitter:title', content: `Sora - Search results for ${params.peopleKeyword}` },\n  ];\n});\n\nexport const handle: Handle = {\n  breadcrumb: ({ match }) => (\n    <BreadcrumbItem\n      to={`/search/people/${match.params.peopleKeyword}`}\n      key={`search-people-${match.params.peopleKeyword}`}\n    >\n      {match.params.peopleKeyword}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ match }) => ({\n    title: 'Search results',\n    subtitle: match.params.peopleKeyword,\n    showImage: false,\n  }),\n};\n\nconst SearchRoute = () => {\n  const { searchResults } = useLoaderData<typeof loader>() || {};\n  const navigate = useNavigate();\n  const location = useLocation();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate('/search/anime');\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      return;\n    }\n  };\n\n  const onSubmit = (value: string) => {\n    navigate(`/search/people/${value}`);\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <SearchForm\n        onSubmit={onSubmit}\n        textHelper={t('search.helper.people')}\n        textOnButton={t('search.action')}\n        textPlaceHolder={t('search.placeHolder.people')}\n      />\n      <MediaList\n        currentPage={searchResults?.page}\n        items={searchResults?.items}\n        itemsType=\"people\"\n        listName={t('search.searchResults')}\n        listType=\"grid\"\n        totalPages={searchResults?.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default SearchRoute;\n"
  },
  {
    "path": "app/routes/search+/people+/_index.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getListPeople } from '~/services/tmdb/tmdb.server';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport SearchForm from '~/components/elements/SearchForm';\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page')) || undefined;\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  return json(\n    {\n      people: await getListPeople('popular', locale, page),\n    },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.trending },\n    },\n  );\n};\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Search People' },\n  { name: 'description', content: 'Search People on Sora' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/search/people/' },\n  { property: 'og:title', content: 'Sora - Search People' },\n  { property: 'og:description', content: 'Search People on Sora' },\n  { name: 'twitter:title', content: 'Sora - Search People' },\n  { name: 'twitter:description', content: 'Search People on Sora' },\n]);\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/search/people\" key=\"search-people\">\n      Search People\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Search',\n    subtitle: 'People',\n    showImage: false,\n  }),\n};\n\nconst SearchRoute = () => {\n  const { people } = useLoaderData<typeof loader>() || {};\n  const navigate = useNavigate();\n  const location = useLocation();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate('/search/anime');\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      return;\n    }\n  };\n\n  const onSubmit = (value: string) => {\n    navigate(`/search/people/${value}`);\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <SearchForm\n        onSubmit={onSubmit}\n        textHelper={t('search.helper.people')}\n        textOnButton={t('search.action')}\n        textPlaceHolder={t('search.placeHolder.people')}\n      />\n      <MediaList\n        currentPage={people?.page}\n        items={people?.items}\n        itemsType=\"people\"\n        listName={t('popular-people')}\n        listType=\"grid\"\n        totalPages={people?.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default SearchRoute;\n"
  },
  {
    "path": "app/routes/search+/tv+/$tvKeyword.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getSearchTvShows } from '~/services/tmdb/tmdb.server';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport SearchForm from '~/components/elements/SearchForm';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const keyword = params?.tvKeyword || '';\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page')) || undefined;\n  if (page && (page < 1 || page > 1000)) page = 1;\n  return json(\n    {\n      searchResults: await getSearchTvShows(keyword, page, locale),\n    },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.search },\n    },\n  );\n};\n\nexport const meta = mergeMeta(({ data, params }) => {\n  // @ts-expect-error\n  const { searchResults } = data;\n  return [\n    { title: `Sora - Search results for ${params.tvKeyword}` },\n    {\n      name: 'keywords',\n      content: `Watch ${params.tvKeyword}, Stream ${params.tvKeyword}, Watch ${params.tvKeyword} HD, Online ${params.tvKeyword}, Streaming ${params.tvKeyword}, English, Subtitle ${params.tvKeyword}, English Subtitle`,\n    },\n    { property: 'og:url', content: `https://sorachill.vercel.app/search/tv/${params.tvKeyword}` },\n    { property: 'og:title', content: `Sora - Search results for ${params.tvKeyword}` },\n    {\n      property: 'og:image',\n      content: searchResults?.items[0]?.backdropPath || searchResults?.items[0]?.posterPath || '',\n    },\n    {\n      name: 'twitter:image',\n      content: searchResults?.items[0]?.backdropPath || searchResults?.items[0]?.posterPath || '',\n    },\n    { name: 'twitter:title', content: `Sora - Search results for ${params.tvKeyword}` },\n  ];\n});\n\nexport const handle: Handle = {\n  breadcrumb: ({ match }) => (\n    <BreadcrumbItem\n      to={`/search/tv/${match.params.tvKeyword}`}\n      key={`search-tv-${match.params.tvKeyword}`}\n    >\n      {match.params.tvKeyword}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ match }) => ({\n    title: 'Search results',\n    subtitle: match.params.tvKeyword,\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst SearchRoute = () => {\n  const { searchResults } = useLoaderData<typeof loader>() || {};\n  const rootData = useTypedRouteLoaderData('root');\n  const navigate = useNavigate();\n  const location = useLocation();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate('/search/movie');\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate('/search/anime');\n    }\n  };\n\n  const onSubmit = (value: string) => {\n    navigate(`/search/tv/${value}`);\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <SearchForm\n        onSubmit={onSubmit}\n        textHelper={t('search.helper.tv')}\n        textOnButton={t('search.action')}\n        textPlaceHolder={t('search.placeHolder.tv')}\n      />\n      <MediaList\n        currentPage={searchResults?.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={searchResults?.items}\n        itemsType=\"tv\"\n        listName={t('search.searchResults')}\n        listType=\"grid\"\n        showListTypeChangeButton\n        totalPages={searchResults?.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default SearchRoute;\n"
  },
  {
    "path": "app/routes/search+/tv+/_index.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getTrending } from '~/services/tmdb/tmdb.server';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport SearchForm from '~/components/elements/SearchForm';\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page')) || undefined;\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  return json(\n    {\n      todayTrending: await getTrending('all', 'day', locale, page),\n    },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.trending },\n    },\n  );\n};\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Search Tv Shows' },\n  { name: 'description', content: 'Search Tv Shows on Sora' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/search/tv/' },\n  { property: 'og:title', content: 'Sora - Search Tv Shows' },\n  { property: 'og:description', content: 'Search Tv Shows on Sora' },\n  { name: 'twitter:title', content: 'Sora - Search Tv Shows' },\n  { name: 'twitter:description', content: 'Search Tv Shows on Sora' },\n]);\n\nexport const handle: Handle = {\n  breadcrumb: () => (\n    <BreadcrumbItem to=\"/search/tv\" key=\"search-tv\">\n      Search Tv Shows\n    </BreadcrumbItem>\n  ),\n  miniTitle: () => ({\n    title: 'Search',\n    subtitle: 'Tv Shows',\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst SearchRoute = () => {\n  const { todayTrending } = useLoaderData<typeof loader>() || {};\n  const rootData = useTypedRouteLoaderData('root');\n  const navigate = useNavigate();\n  const location = useLocation();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate('/search/movie');\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate('/search/anime');\n    }\n  };\n\n  const onSubmit = (value: string) => {\n    navigate(`/search/tv/${value}`);\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <SearchForm\n        onSubmit={onSubmit}\n        textHelper={t('search.helper.tv')}\n        textOnButton={t('search.action')}\n        textPlaceHolder={t('search.placeHolder.tv')}\n      />\n      <MediaList\n        currentPage={todayTrending?.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={todayTrending?.items}\n        itemsType=\"movie-tv\"\n        listName={t('today-trending')}\n        listType=\"grid\"\n        showListTypeChangeButton\n        totalPages={todayTrending?.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default SearchRoute;\n"
  },
  {
    "path": "app/routes/search.tsx",
    "content": "import { Outlet } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport { searchPages } from '~/constants/tabLinks';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Search' },\n  { name: 'description', content: 'Search Movies, Tv Series and Anime on Sora' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/search' },\n  { property: 'og:title', content: 'Sora - Search' },\n  { property: 'og:image', content: 'https://sorachill.vercel.app/api/ogimage?it=search' },\n  { property: 'og:description', content: 'Search Movies, Tv Series and Anime on Sora' },\n  { name: 'twitter:title', content: 'Sora - Search' },\n  { name: 'twitter:image', content: 'https://sorachill.vercel.app/api/ogimage?it=search' },\n  { name: 'twitter:description', content: 'Search Movies, Tv Series and Anime on Sora' },\n]);\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/search\" key=\"search\">\n      {t('search.action')}\n    </BreadcrumbItem>\n  ),\n  showTabLink: true,\n  tabLinkPages: searchPages,\n  tabLinkTo: () => '/search/',\n  miniTitle: ({ t }) => ({\n    title: t('search.action'),\n    showImage: false,\n  }),\n};\n\nconst SearchPage = () => <Outlet />;\n\nexport default SearchPage;\n"
  },
  {
    "path": "app/routes/settings.tsx",
    "content": "import { Fragment, useEffect, useRef, useState } from 'react';\nimport { Accordion, AccordionItem } from '@nextui-org/accordion';\nimport { Kbd, type KbdKey } from '@nextui-org/kbd';\nimport { Link } from '@nextui-org/link';\nimport { Spacer } from '@nextui-org/spacer';\nimport { Spinner } from '@nextui-org/spinner';\nimport { Switch, type SwitchProps } from '@nextui-org/switch';\nimport { useLocalStorageValue, useMediaQuery } from '@react-hookz/web';\nimport { NavLink, Link as RemixLink, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { AnimatePresence, motion, type PanInfo } from 'framer-motion';\nimport { useTheme } from 'next-themes';\nimport { isMobileOnly } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\nimport { MimeType } from 'remix-image';\n\nimport type { Handle } from '~/types/handle';\nimport { getBackgroundTitleBarColor, setMetaThemeColor } from '~/utils/client/meta-tags.client';\nimport { ClientOnly } from '~/utils/react/ClientOnly';\nimport useColorDarkenLighten from '~/utils/react/hooks/useColorDarkenLighten';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport languages from '~/constants/languages';\nimport {\n  listCustomThemeColors,\n  listDefaultThemeColors,\n  listListLoadingType,\n  listListViewType,\n  listSubtitleBackgroundColor,\n  listSubtitleBackgroundOpacity,\n  listSubtitleFontColor,\n  listSubtitleFontSize,\n  listSubtitleTextEffects,\n  // listSidebarActiveStyleMode,\n  listSubtitleWindowColor,\n  listSubtitleWindowOpacity,\n  // listThemes,\n  settingsTab,\n} from '~/constants/settings';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport Image from '~/components/elements/Image';\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '~/components/elements/Select';\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '~/components/elements/tab/Tabs';\nimport Brush from '~/assets/icons/BrushIcon';\nimport Info from '~/assets/icons/InfoIcon';\nimport Moon from '~/assets/icons/MoonIcon';\nimport Play from '~/assets/icons/PlayIcon';\nimport SettingsIcon from '~/assets/icons/SettingsIcon';\nimport Sun from '~/assets/icons/SunIcon';\nimport User from '~/assets/icons/UserIcon';\nimport LogoFooter from '~/assets/images/logo_footer.png';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Settings' },\n  { name: 'description', content: 'Settings' },\n  { property: 'og:title', content: 'Settings' },\n  { property: 'og:description', content: 'Settings' },\n  { name: 'twitter:title', content: 'Settings' },\n  { name: 'twitter:description', content: 'Settings' },\n]);\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/settings\" key=\"settings\">\n      {t('settings')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('settings'),\n    showImage: false,\n  }),\n};\n\nconst settingsIcon = (id: string, filled: boolean) => {\n  let icon;\n  switch (id) {\n    case 'general-tab':\n      icon = <SettingsIcon filled={filled} />;\n      break;\n    case 'appearance-tab':\n      icon = <Brush filled={filled} />;\n      break;\n    case 'account-tab':\n      icon = <User filled={filled} />;\n      break;\n    case 'player-tab':\n      icon = <Play filled={filled} />;\n      break;\n    case 'about-tab':\n      icon = <Info filled={filled} />;\n      break;\n    default:\n  }\n  return icon;\n};\n\ninterface SettingBlockCommonProps {\n  title: string;\n}\n\ninterface SettingBlockSelectProps extends SettingBlockCommonProps {\n  type: 'select';\n  selectedValue?: string;\n  onSelectionChange: (value: string) => void;\n  selectItems: string[];\n}\n\ninterface SettingBlockSwitchProps extends SettingBlockCommonProps {\n  type: 'switch';\n  description?: string;\n}\n\ninterface SettingBlockKbdProps extends SettingBlockCommonProps {\n  type: 'kbd';\n  keys?:\n    | KbdKey\n    | KbdKey[]\n    | {\n        keys?: KbdKey | KbdKey[];\n        key?: string | number;\n        id: string;\n      }[];\n  kbd?: string | number;\n  betweenKeys?: string;\n}\n\ntype SettingBlockProps =\n  | SettingBlockSelectProps\n  | (SettingBlockSwitchProps & SwitchProps)\n  | SettingBlockKbdProps;\n\nconst SettingBlock = (props: SettingBlockProps) => {\n  const { type } = props;\n  const { t } = useTranslation('settings');\n  if (type === 'switch') {\n    const { title, description, ...rest } = props;\n    return (\n      <div className=\"flex flex-row items-center justify-between gap-x-2 rounded-small bg-content2 p-3\">\n        {description ? (\n          <div className=\"flex flex-col items-start justify-center\">\n            <h6>{title}</h6>\n            <p className=\"opacity-80\">{description}</p>\n          </div>\n        ) : (\n          <h6>{title}</h6>\n        )}\n        <Switch {...rest} />\n      </div>\n    );\n  }\n  if (type === 'select') {\n    const { title, selectedValue, onSelectionChange, selectItems } = props;\n    return (\n      <div className=\"flex flex-row items-center justify-between rounded-small bg-content2 p-3\">\n        <h6>{title}</h6>\n        {selectItems && selectItems.length > 0 ? (\n          <Select value={selectedValue} onValueChange={(value) => onSelectionChange(value)}>\n            <SelectTrigger arial-label={title} className=\"!w-fit\">\n              <SelectValue />\n            </SelectTrigger>\n            <SelectContent>\n              {selectItems.map((item) => (\n                <SelectItem key={item} value={item}>\n                  {t(item)}\n                </SelectItem>\n              ))}\n            </SelectContent>\n          </Select>\n        ) : null}\n      </div>\n    );\n  }\n  if (type === 'kbd') {\n    const { keys, kbd, title, betweenKeys } = props;\n    return (\n      <div className=\"flex flex-row items-center justify-between gap-x-2 rounded-small bg-content2 p-3\">\n        <h6>{title}</h6>\n        {keys ? (\n          Array.isArray(\n            keys as {\n              keys?: KbdKey | KbdKey[];\n              key?: string | number;\n              id: string;\n            }[],\n          ) ? (\n            <div className=\"flex flex-row items-center gap-x-2\">\n              {(\n                keys as {\n                  keys?: KbdKey | KbdKey[];\n                  key?: string | number;\n                  id: string;\n                }[]\n              ).map((k, index) => (\n                <Fragment key={k.id}>\n                  {k?.keys ? (\n                    <Kbd keys={k?.keys as KbdKey | KbdKey[]}>{k?.key ? k?.key : null}</Kbd>\n                  ) : (\n                    <Kbd>{k?.key ? k?.key : null}</Kbd>\n                  )}\n                  {index !== (keys as KbdKey[]).length - 1 && betweenKeys ? (\n                    <div>{betweenKeys}</div>\n                  ) : null}\n                </Fragment>\n              ))}\n            </div>\n          ) : typeof keys === 'string' ? (\n            <Kbd keys={keys as KbdKey | KbdKey[]}>{kbd || ''}</Kbd>\n          ) : null\n        ) : (\n          <Kbd>{kbd || ''}</Kbd>\n        )}\n      </div>\n    );\n  }\n  return null;\n};\n\nconst Settings = () => {\n  const location = useLocation();\n  const navigate = useNavigate();\n  const isHydrated = useHydrated();\n  const rootData = useTypedRouteLoaderData('root');\n  const { locale } = rootData || { locale: 'en' };\n  const { t } = useTranslation('settings');\n  const { theme, setTheme } = useTheme();\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const isMd = useMediaQuery('(max-width: 1280px)', { initializeWithValue: false });\n  const underlineRef = useRef<HTMLDivElement>(null);\n  const { isDark } = useColorDarkenLighten();\n\n  const {\n    currentSubtitleFontColor,\n    currentSubtitleFontSize,\n    currentSubtitleBackgroundColor,\n    currentSubtitleBackgroundOpacity,\n    currentSubtitleWindowColor,\n    currentSubtitleWindowOpacity,\n    currentSubtitleTextEffects,\n    autoShowSubtitle,\n    // showFilter,\n    isMutedTrailer,\n    isPlayTrailer,\n    isAutoSize,\n    isPicInPic,\n    isMuted,\n    isAutoPlay,\n    isAutoMini,\n    isLoop,\n    isScreenshot,\n    isMiniProgressBar,\n    isAutoPlayback,\n    isAutoPlayNextEpisode,\n    isShowSkipOpEdButton,\n    isAutoSkipOpEd,\n    isFastForward,\n    // isSwipeFullscreen,\n    // sidebarStyleMode,\n    sidebarMiniMode,\n    sidebarHoverMode,\n    sidebarBoxedMode,\n    // sidebarSheetMode,\n    autoSwitchSubtitle,\n    isShowBreadcrumb,\n    isShowTopPagination,\n    isLightDarkThemeOnly,\n    isFetchLogo,\n    isShowSpotlight,\n  } = useSoraSettings();\n  const listViewType = useLocalStorageValue('sora_settings-layout-list_view', {\n    defaultValue: 'card',\n  });\n  const listLoadingType = useLocalStorageValue('sora_settings-layout-list-loading_type', {\n    defaultValue: 'pagination',\n  });\n  const currentThemeColor = useLocalStorageValue('sora_settings-layout-theme-color', {\n    defaultValue: 'blue',\n  });\n\n  const [activeTab, setActiveTab] = useState('general-tab');\n  const [selectedLang, setSelectedLang] = useState(locale);\n  const [selectedSubtitleFontColor, setSelectedSubtitleFontColor] = useState(\n    currentSubtitleFontColor.value,\n  );\n  const [selectedSubtitleFontSize, setSelectedSubtitleFontSize] = useState(\n    currentSubtitleFontSize.value,\n  );\n  const [selectedSubtitleBackgroundColor, setSelectedSubtitleBackgroundColor] = useState(\n    currentSubtitleBackgroundColor.value,\n  );\n  const [selectedSubtitleBackgroundOpacity, setSelectedSubtitleBackgroundOpacity] = useState(\n    currentSubtitleBackgroundOpacity.value,\n  );\n  const [selectedSubtitleWindowColor, setSelectedSubtitleWindowColor] = useState(\n    currentSubtitleWindowColor.value,\n  );\n  const [selectedSubtitleWindowOpacity, setSelectedSubtitleWindowOpacity] = useState(\n    currentSubtitleWindowOpacity.value,\n  );\n  const [selectedSubtitleTextEffects, setSelectedSubtitleTextEffects] = useState(\n    currentSubtitleTextEffects.value,\n  );\n  const [selectedListViewType, setSelectedListViewType] = useState(listViewType.value);\n  const [selectedListLoadingType, setSelectedListLoadingType] = useState(listLoadingType.value);\n  const [selectedThemeColor, setSelectedThemeColor] = useState(() => {\n    if (!isLightDarkThemeOnly.value) {\n      return theme;\n    } else {\n      return currentThemeColor.value;\n    }\n  });\n\n  useEffect(() => {\n    if (underlineRef.current) {\n      underlineRef.current.scrollIntoView({\n        behavior: 'smooth',\n        block: 'center',\n        inline: 'center',\n      });\n    }\n  }, [activeTab]);\n\n  const handleDragEnd = (_event: MouseEvent | PointerEvent | TouchEvent, info: PanInfo) => {\n    const currentTab = settingsTab.find((tab) => tab.id === activeTab);\n    if (info.offset?.x > 100) {\n      // swipe right\n      if (currentTab?.id === 'general-tab') {\n        setActiveTab('about-tab');\n      } else {\n        const index = settingsTab.findIndex((tab) => tab.id === activeTab);\n        setActiveTab(settingsTab[index - 1].id);\n      }\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      // swipe left\n      if (currentTab?.id === 'about-tab') {\n        setActiveTab('general-tab');\n      } else {\n        const index = settingsTab.findIndex((tab) => tab.id === activeTab);\n        setActiveTab(settingsTab[index + 1].id);\n      }\n    }\n  };\n\n  const handleSelect = async (value: string, type: string) => {\n    switch (type) {\n      case 'language': {\n        setSelectedLang(value);\n        navigate(`${location.pathname}?lng=${value}`);\n        break;\n      }\n      case 'list-view-type': {\n        setSelectedListViewType(value);\n        listViewType.set(value);\n        break;\n      }\n      case 'list-loading-type': {\n        setSelectedListLoadingType(value);\n        listLoadingType.set(value);\n        break;\n      }\n      case 'subtitle-font-color': {\n        setSelectedSubtitleFontColor(value);\n        currentSubtitleFontColor.set(value);\n        break;\n      }\n      case 'subtitle-font-size': {\n        setSelectedSubtitleFontSize(value);\n        currentSubtitleFontSize.set(value);\n        break;\n      }\n      case 'subtitle-background-color': {\n        setSelectedSubtitleBackgroundColor(value);\n        currentSubtitleBackgroundColor.set(value);\n        break;\n      }\n      case 'subtitle-background-opacity': {\n        setSelectedSubtitleBackgroundOpacity(value);\n        currentSubtitleBackgroundOpacity.set(value);\n        break;\n      }\n      case 'subtitle-window-color': {\n        setSelectedSubtitleWindowColor(value);\n        currentSubtitleWindowColor.set(value);\n        break;\n      }\n      case 'subtitle-window-opacity': {\n        setSelectedSubtitleWindowOpacity(value);\n        currentSubtitleWindowOpacity.set(value);\n        break;\n      }\n      case 'subtitle-text-effects': {\n        setSelectedSubtitleTextEffects(value);\n        currentSubtitleTextEffects.set(value);\n        break;\n      }\n      case 'theme-color': {\n        await setSelectedThemeColor(value);\n        await currentThemeColor.set(value);\n        if (isDark) {\n          if (value !== 'blue') {\n            await setTheme(`dark-${value}`);\n          } else {\n            await setTheme('dark');\n          }\n        } else {\n          if (value !== 'blue') {\n            await setTheme(`light-${value}`);\n          } else {\n            await setTheme('light');\n          }\n        }\n        const color = await getBackgroundTitleBarColor(isHydrated);\n        await setMetaThemeColor(`hsl(${color})`);\n        break;\n      }\n      case 'custom-theme-color': {\n        await setSelectedThemeColor(value);\n        await setTheme(value);\n        const color = await getBackgroundTitleBarColor(isHydrated);\n        await setMetaThemeColor(`hsl(${color})`);\n      }\n    }\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ x: '-10%', opacity: 0 }}\n      animate={{ x: '0', opacity: 1 }}\n      exit={{ y: '-10%', opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full max-w-screen-4xl flex-col justify-start py-3 sm:py-0\"\n    >\n      <h2>{t('settings')}</h2>\n      <Spacer y={2.5} />\n      <ClientOnly fallback={<Spinner />}>\n        {() => (\n          <Tabs\n            defaultValue={activeTab}\n            value={activeTab}\n            orientation={isSm ? 'horizontal' : 'vertical'}\n            onValueChange={(value) => setActiveTab(value)}\n          >\n            <TabsList>\n              {settingsTab.map((tab) => (\n                <TabsTrigger\n                  key={tab.id}\n                  value={tab.id}\n                  disabled={tab.disabled}\n                  className=\"relative\"\n                >\n                  {settingsIcon(tab.id, activeTab === tab.id)}\n                  <h6 className=\"ml-2\">{t(tab.title)}</h6>\n                  {activeTab === tab.id ? (\n                    <motion.div\n                      className=\"absolute overflow-hidden rounded-small bg-default-foreground data-[orientation=horizontal]:bottom-0 data-[orientation=vertical]:left-0 data-[orientation=horizontal]:h-1 data-[orientation=vertical]:h-1/2 data-[orientation=horizontal]:w-1/2 data-[orientation=vertical]:w-1\"\n                      layoutId=\"underline\"\n                      data-orientation={isSm ? 'horizontal' : 'vertical'}\n                      ref={underlineRef}\n                    />\n                  ) : null}\n                </TabsTrigger>\n              ))}\n            </TabsList>\n            <AnimatePresence mode=\"wait\">\n              <TabsContent value=\"general-tab\" key=\"general-tab\" asChild>\n                <motion.div\n                  initial={{ opacity: 0 }}\n                  animate={{ opacity: 1 }}\n                  exit={{ opacity: 0 }}\n                  transition={{ duration: 0.75 }}\n                  drag={isMobileOnly ? 'x' : false}\n                  dragConstraints={{ left: 0, right: 0 }}\n                  dragElastic={0.4}\n                  onDragEnd={handleDragEnd}\n                  dragDirectionLock\n                >\n                  <div className=\"flex w-full flex-col justify-start rounded-medium bg-content1 p-5 shadow-medium\">\n                    <SettingBlock\n                      type=\"select\"\n                      title={t('language')}\n                      selectedValue={selectedLang}\n                      onSelectionChange={(value) => handleSelect(value, 'language')}\n                      selectItems={languages}\n                    />\n                  </div>\n                </motion.div>\n              </TabsContent>\n              <TabsContent value=\"appearance-tab\" key=\"appearance-tab\" asChild>\n                <motion.div\n                  initial={{ opacity: 0 }}\n                  animate={{ opacity: 1 }}\n                  exit={{ opacity: 0 }}\n                  transition={{ duration: 0.75 }}\n                  drag={isMobileOnly ? 'x' : false}\n                  dragConstraints={{ left: 0, right: 0 }}\n                  dragElastic={0.4}\n                  onDragEnd={handleDragEnd}\n                  className=\"w-full\"\n                >\n                  {/* @ts-ignore */}\n                  <Accordion variant=\"shadow\" selectionMode=\"multiple\">\n                    <AccordionItem\n                      title={t('theme')}\n                      subtitle={t('theme-subtitle')}\n                      classNames={{\n                        title: 'text-2xl',\n                        subtitle: 'text-base',\n                        content: 'pb-4',\n                      }}\n                    >\n                      <SettingBlock\n                        type=\"switch\"\n                        title={t('light-dark-only')}\n                        isSelected={isLightDarkThemeOnly.value}\n                        onValueChange={async (isSelected) => {\n                          await isLightDarkThemeOnly.set(isSelected);\n                          if (isSelected) {\n                            await setTheme('system');\n                            await setSelectedThemeColor('blue');\n                          } else {\n                            await setTheme('bumblebee');\n                            await setSelectedThemeColor('bumblebee');\n                          }\n                          const color = await getBackgroundTitleBarColor(isHydrated);\n                          await setMetaThemeColor(`hsl(${color})`);\n                        }}\n                      />\n                      <Spacer y={2.5} />\n                      {isLightDarkThemeOnly.value ? (\n                        <>\n                          <SettingBlock\n                            type=\"switch\"\n                            title={t('dark-mode')}\n                            isSelected={isDark}\n                            onValueChange={async (isSelected) => {\n                              if (isSelected) {\n                                if (currentThemeColor.value !== 'blue') {\n                                  await setTheme(`dark-${currentThemeColor.value}`);\n                                } else await setTheme('dark');\n                              } else {\n                                if (currentThemeColor.value !== 'blue') {\n                                  await setTheme(`light-${currentThemeColor.value}`);\n                                } else await setTheme('light');\n                              }\n                              const color = await getBackgroundTitleBarColor(isHydrated);\n                              await setMetaThemeColor(`hsl(${color})`);\n                            }}\n                            color=\"primary\"\n                            classNames={{\n                              thumbIcon: 'h-4 w-4',\n                            }}\n                            thumbIcon={({ isSelected, className }) =>\n                              isSelected ? (\n                                <Moon className={className} />\n                              ) : (\n                                <Sun className={className} />\n                              )\n                            }\n                          />\n                          <Spacer y={2.5} />\n                          <SettingBlock\n                            type=\"select\"\n                            title={t('theme-color')}\n                            selectedValue={selectedThemeColor}\n                            onSelectionChange={(value) => handleSelect(value, 'theme-color')}\n                            selectItems={listDefaultThemeColors}\n                          />\n                        </>\n                      ) : (\n                        <SettingBlock\n                          type=\"select\"\n                          title={t('custom-theme-color')}\n                          selectedValue={selectedThemeColor}\n                          onSelectionChange={(value) => handleSelect(value, 'custom-theme-color')}\n                          selectItems={listCustomThemeColors}\n                        />\n                      )}\n                    </AccordionItem>\n                    {isSm ? null : (\n                      <AccordionItem\n                        title={t('sidebar')}\n                        subtitle={t('sidebar-subtitle')}\n                        classNames={{\n                          title: 'text-2xl',\n                          subtitle: 'text-base',\n                          content: 'pb-4',\n                        }}\n                      >\n                        <div className=\"flex flex-col items-start justify-center gap-y-4 rounded-small bg-content2 p-3\">\n                          <h5 className=\"my-1\">{t('sidebar-mode')}</h5>\n                          {isMd ? null : (\n                            <>\n                              <div className=\"flex w-full flex-row items-center justify-between gap-x-2\">\n                                <h6>{t('sidebar-mini-mode')}</h6>\n                                <Switch\n                                  isSelected={sidebarMiniMode.value}\n                                  onValueChange={(isSelected: boolean) => {\n                                    sidebarMiniMode.set(isSelected);\n                                    if (sidebarMiniMode.value) {\n                                      sidebarHoverMode.set(false);\n                                    }\n                                  }}\n                                />\n                              </div>\n                              <div className=\"flex w-full flex-row items-center justify-between gap-x-2\">\n                                <h6>{t('sidebar-hover-mode')}</h6>\n                                <Switch\n                                  isSelected={sidebarHoverMode.value}\n                                  onValueChange={(isSelected: boolean) => {\n                                    sidebarHoverMode.set(isSelected);\n                                    if (!sidebarHoverMode.value) {\n                                      sidebarMiniMode.set(true);\n                                    }\n                                  }}\n                                />\n                              </div>\n                            </>\n                          )}\n                          <div className=\"flex w-full flex-row items-center justify-between gap-x-2\">\n                            <h6>{t('sidebar-boxed-mode')}</h6>\n                            <Switch\n                              isSelected={sidebarBoxedMode.value}\n                              onValueChange={(isSelected: boolean) =>\n                                sidebarBoxedMode.set(isSelected)\n                              }\n                            />\n                          </div>\n                        </div>\n                      </AccordionItem>\n                    )}\n                    {isSm ? null : (\n                      <AccordionItem\n                        title={t('header')}\n                        subtitle={t('header-subtitle')}\n                        classNames={{\n                          title: 'text-2xl',\n                          subtitle: 'text-base',\n                          content: 'pb-4',\n                        }}\n                      >\n                        <SettingBlock\n                          type=\"switch\"\n                          title={t('show-breadcrumb')}\n                          isSelected={isShowBreadcrumb.value}\n                          onValueChange={(isSelected) => isShowBreadcrumb.set(isSelected)}\n                        />\n                      </AccordionItem>\n                    )}\n                    {isSm ? null : (\n                      <AccordionItem\n                        title={t('media-list-banner')}\n                        subtitle={t('media-list-banner-subtitle')}\n                        classNames={{\n                          title: 'text-2xl',\n                          subtitle: 'text-base',\n                          content: 'pb-4',\n                        }}\n                      >\n                        <SettingBlock\n                          type=\"switch\"\n                          title={t('show-logo')}\n                          isSelected={isFetchLogo.value}\n                          onValueChange={(isSelected) => isFetchLogo.set(isSelected)}\n                        />\n                        <Spacer y={2.5} />\n                        <SettingBlock\n                          type=\"switch\"\n                          title={t('show-spotlight')}\n                          isSelected={isShowSpotlight.value}\n                          onValueChange={(isSelected) => isShowSpotlight.set(isSelected)}\n                        />\n                        <Spacer y={2.5} />\n                        <SettingBlock\n                          type=\"switch\"\n                          title={t('play-trailer')}\n                          isSelected={isPlayTrailer.value}\n                          onValueChange={(isSelected) => isPlayTrailer.set(isSelected)}\n                        />\n                        <Spacer y={2.5} />\n                        <SettingBlock\n                          type=\"switch\"\n                          title={t('mute-trailer')}\n                          isSelected={isMutedTrailer.value}\n                          onValueChange={(isSelected) => isMutedTrailer.set(isSelected)}\n                        />\n                      </AccordionItem>\n                    )}\n                    <AccordionItem\n                      title={t('media-list-grid')}\n                      subtitle={t('media-list-grid-subtitle')}\n                      classNames={{\n                        title: 'text-2xl',\n                        subtitle: 'text-base',\n                        content: 'pb-4',\n                      }}\n                    >\n                      <SettingBlock\n                        type=\"select\"\n                        title={t('list-view-type')}\n                        selectedValue={selectedListViewType}\n                        onSelectionChange={(value) => handleSelect(value, 'list-view-type')}\n                        selectItems={listListViewType}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"select\"\n                        title={t('list-loading-type')}\n                        selectedValue={selectedListLoadingType}\n                        onSelectionChange={(value) => handleSelect(value, 'list-loading-type')}\n                        selectItems={listListLoadingType}\n                      />\n                      <Spacer y={2.5} />\n                      {selectedListLoadingType === 'pagination' ? (\n                        <SettingBlock\n                          type=\"switch\"\n                          title={t('show-top-pagination')}\n                          isSelected={isShowTopPagination.value}\n                          onValueChange={(isSelected) => isShowTopPagination.set(isSelected)}\n                        />\n                      ) : null}\n                    </AccordionItem>\n                  </Accordion>\n                </motion.div>\n              </TabsContent>\n              <TabsContent value=\"player-tab\" key=\"player-tab\" asChild>\n                <motion.div\n                  initial={{ opacity: 0 }}\n                  animate={{ opacity: 1 }}\n                  exit={{ opacity: 0 }}\n                  transition={{ duration: 0.75 }}\n                  drag={isMobileOnly ? 'x' : false}\n                  dragConstraints={{ left: 0, right: 0 }}\n                  dragElastic={0.4}\n                  onDragEnd={handleDragEnd}\n                  className=\"w-full\"\n                >\n                  <Accordion variant=\"shadow\" selectionMode=\"multiple\">\n                    <AccordionItem\n                      title={t('defaults')}\n                      subtitle={t('defaults-subtitle')}\n                      classNames={{\n                        title: 'text-2xl',\n                        subtitle: 'text-base',\n                        content: 'pb-4',\n                      }}\n                    >\n                      <SettingBlock\n                        type=\"switch\"\n                        title={t('mute-trailer')}\n                        description={t('pic-in-pic-subtitle')}\n                        isSelected={isPicInPic.value}\n                        onValueChange={(isSelected) => isPicInPic.set(isSelected)}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"switch\"\n                        title={t('muted')}\n                        description={t('muted-subtitle')}\n                        isSelected={isMuted.value}\n                        onValueChange={(isSelected) => isMuted.set(isSelected)}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"switch\"\n                        title={t('autoplay')}\n                        description={t('autoplay-subtitle')}\n                        isSelected={isAutoPlay.value}\n                        onValueChange={(isSelected) => isAutoPlay.set(isSelected)}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"switch\"\n                        title={t('loop')}\n                        description={t('loop-subtitle')}\n                        isSelected={isLoop.value}\n                        onValueChange={(isSelected) => isLoop.set(isSelected)}\n                      />\n                    </AccordionItem>\n                    <AccordionItem\n                      title={t('subtitles')}\n                      subtitle={t('subtitles-subtitle')}\n                      classNames={{\n                        title: 'text-2xl',\n                        subtitle: 'text-base',\n                        content: 'pb-4',\n                      }}\n                    >\n                      <SettingBlock\n                        type=\"switch\"\n                        title={t('show-subtitle')}\n                        description={t('show-subtitle-subtitle')}\n                        isSelected={autoShowSubtitle.value}\n                        onValueChange={(isSelected) => autoShowSubtitle.set(isSelected)}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"switch\"\n                        title={t('auto-switch-subtitle')}\n                        description={t('auto-switch-subtitle-subtitle')}\n                        isSelected={autoSwitchSubtitle.value}\n                        onValueChange={(isSelected) => autoSwitchSubtitle.set(isSelected)}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"select\"\n                        title={t('subtitle-font-color')}\n                        selectedValue={selectedSubtitleFontColor}\n                        onSelectionChange={(value) => handleSelect(value, 'subtitle-font-color')}\n                        selectItems={listSubtitleFontColor}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"select\"\n                        title={t('subtitle-font-size')}\n                        selectedValue={selectedSubtitleFontSize}\n                        onSelectionChange={(value) => handleSelect(value, 'subtitle-font-size')}\n                        selectItems={listSubtitleFontSize}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"select\"\n                        title={t('subtitle-background-color')}\n                        selectedValue={selectedSubtitleBackgroundColor}\n                        onSelectionChange={(value) =>\n                          handleSelect(value, 'subtitle-background-color')\n                        }\n                        selectItems={listSubtitleBackgroundColor}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"select\"\n                        title={t('subtitle-background-opacity')}\n                        selectedValue={selectedSubtitleBackgroundOpacity}\n                        onSelectionChange={(value) =>\n                          handleSelect(value, 'subtitle-background-opacity')\n                        }\n                        selectItems={listSubtitleBackgroundOpacity}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"select\"\n                        title={t('subtitle-window-color')}\n                        selectedValue={selectedSubtitleWindowColor}\n                        onSelectionChange={(value) => handleSelect(value, 'subtitle-window-color')}\n                        selectItems={listSubtitleWindowColor}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"select\"\n                        title={t('subtitle-window-opacity')}\n                        selectedValue={selectedSubtitleWindowOpacity}\n                        onSelectionChange={(value) =>\n                          handleSelect(value, 'subtitle-window-opacity')\n                        }\n                        selectItems={listSubtitleWindowOpacity}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"select\"\n                        title={t('subtitle-text-effects')}\n                        selectedValue={selectedSubtitleTextEffects}\n                        onSelectionChange={(value) => handleSelect(value, 'subtitle-text-effects')}\n                        selectItems={listSubtitleTextEffects}\n                      />\n                    </AccordionItem>\n                    <AccordionItem\n                      title={t('player-features')}\n                      subtitle={t('player-features-subtitle')}\n                      classNames={{\n                        title: 'text-2xl',\n                        subtitle: 'text-base',\n                        content: 'pb-4',\n                      }}\n                    >\n                      <SettingBlock\n                        type=\"switch\"\n                        title={t('auto-size')}\n                        description={t('auto-size-subtitle')}\n                        isSelected={isAutoSize.value}\n                        onValueChange={(isSelected) => isAutoSize.set(isSelected)}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"switch\"\n                        title={t('auto-mini')}\n                        description={t('auto-mini-subtitle')}\n                        isSelected={isAutoMini.value}\n                        onValueChange={(isSelected) => isAutoMini.set(isSelected)}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"switch\"\n                        title={t('screenshot')}\n                        description={t('screenshot-subtitle')}\n                        isSelected={isScreenshot.value}\n                        onValueChange={(isSelected) => isScreenshot.set(isSelected)}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"switch\"\n                        title={t('mini-progressbar')}\n                        description={t('mini-progressbar-subtitle')}\n                        isSelected={isMiniProgressBar.value}\n                        onValueChange={(isSelected) => isMiniProgressBar.set(isSelected)}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"switch\"\n                        title={t('auto-playback')}\n                        description={t('auto-playback-subtitle')}\n                        isSelected={isAutoPlayback.value}\n                        onValueChange={(isSelected) => isAutoPlayback.set(isSelected)}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"switch\"\n                        title={t('fast-forward')}\n                        description={t('fast-forward-subtitle')}\n                        isSelected={isFastForward.value}\n                        onValueChange={(isSelected) => isFastForward.set(isSelected)}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"switch\"\n                        title={t('auto-play-next-episode')}\n                        description={t('auto-play-next-episode-subtitle')}\n                        isSelected={isAutoPlayNextEpisode.value}\n                        onValueChange={(isSelected) => isAutoPlayNextEpisode.set(isSelected)}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"switch\"\n                        title={t('show-skip-op-ed-button')}\n                        description={t('show-skip-op-ed-button-subtitle')}\n                        isSelected={isShowSkipOpEdButton.value}\n                        onValueChange={(isSelected) => {\n                          isShowSkipOpEdButton.set(isSelected);\n                          if (!isShowSkipOpEdButton.value) {\n                            isAutoSkipOpEd.set(false);\n                          }\n                        }}\n                      />\n                      <AnimatePresence>\n                        {isShowSkipOpEdButton ? (\n                          <motion.div\n                            initial={{ y: -20, opacity: 0 }}\n                            animate={{ y: 0, opacity: 1 }}\n                            exit={{ y: -20, opacity: 0 }}\n                            transition={{ duration: 0.3 }}\n                          >\n                            <Spacer y={2.5} />\n                            <SettingBlock\n                              type=\"switch\"\n                              title={t('auto-skip-op-ed')}\n                              description={t('auto-skip-op-ed-subtitle')}\n                              isSelected={isAutoSkipOpEd.value}\n                              onValueChange={(isSelected) => isAutoSkipOpEd.set(isSelected)}\n                            />\n                          </motion.div>\n                        ) : null}\n                      </AnimatePresence>\n                    </AccordionItem>\n                    <AccordionItem\n                      title={t('keyboard')}\n                      subtitle={t('keyboard-subtitle')}\n                      classNames={{\n                        title: 'text-2xl',\n                        subtitle: 'text-base',\n                        content: 'pb-4',\n                      }}\n                    >\n                      <SettingBlock type=\"kbd\" title={t('volume-up')} keys=\"up\" />\n                      <Spacer y={2.5} />\n                      <SettingBlock type=\"kbd\" title={t('volume-down')} keys=\"down\" />\n                      <Spacer y={2.5} />\n                      <SettingBlock type=\"kbd\" title={t('fast-rewind-5s')} keys=\"left\" />\n                      <Spacer y={2.5} />\n                      <SettingBlock type=\"kbd\" title={t('fast-forward-5s')} keys=\"right\" />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"kbd\"\n                        title={t('toggle-play-pause')}\n                        keys={[\n                          { keys: 'space', id: 'space' },\n                          { key: 'K', id: 'K' },\n                        ]}\n                        betweenKeys={t('or')}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"kbd\"\n                        title={t('seek-to-start')}\n                        keys={[\n                          { keys: 'home', id: 'home' },\n                          { key: '0', id: '0' },\n                        ]}\n                        betweenKeys={t('or')}\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock type=\"kbd\" title={t('seek-to-end')} keys=\"end\" />\n                      <Spacer y={2.5} />\n                      <SettingBlock\n                        type=\"kbd\"\n                        title={t('seek-to-percent')}\n                        keys={[\n                          { key: '1', id: '1' },\n                          { key: '9', id: '9' },\n                        ]}\n                        betweenKeys=\"...\"\n                      />\n                      <Spacer y={2.5} />\n                      <SettingBlock type=\"kbd\" title={t('toggle-subtitle')} kbd=\"C\" />\n                      <Spacer y={2.5} />\n                      <SettingBlock type=\"kbd\" title={t('toggle-fullscreen')} kbd=\"F\" />\n                      <Spacer y={2.5} />\n                      <SettingBlock type=\"kbd\" title={t('fast-rewind-10s')} kbd=\"J\" />\n                      <Spacer y={2.5} />\n                      <SettingBlock type=\"kbd\" title={t('fast-forward-10s')} kbd=\"L\" />\n                      <Spacer y={2.5} />\n                      <SettingBlock type=\"kbd\" title={t('mute-unmute')} kbd=\"M\" />\n                    </AccordionItem>\n                  </Accordion>\n                </motion.div>\n              </TabsContent>\n              <TabsContent value=\"about-tab\" key=\"about-tab\" asChild>\n                <motion.div\n                  initial={{ opacity: 0 }}\n                  animate={{ opacity: 1 }}\n                  exit={{ opacity: 0 }}\n                  transition={{ duration: 0.75 }}\n                  drag={isMobileOnly ? 'x' : false}\n                  dragConstraints={{ left: 0, right: 0 }}\n                  dragElastic={0.4}\n                  onDragEnd={handleDragEnd}\n                  className=\"w-full\"\n                >\n                  <div className=\"w-full rounded-large bg-content1 p-5 shadow-lg\">\n                    <div className=\"flex flex-col items-center justify-center\">\n                      <Image\n                        alt=\"About Logo\"\n                        title=\"About Logo\"\n                        src={LogoFooter}\n                        loaderUrl=\"/api/image\"\n                        width=\"76px\"\n                        height=\"76px\"\n                        radius=\"full\"\n                        placeholder=\"empty\"\n                        responsive={[\n                          {\n                            size: {\n                              width: 76,\n                              height: 76,\n                            },\n                          },\n                        ]}\n                        options={{\n                          contentType: MimeType.WEBP,\n                        }}\n                      />\n                      <NavLink\n                        to=\"/\"\n                        arial-label=\"home-page\"\n                        className=\"bg-gradient-to-tr from-secondary to-primary to-50% bg-clip-text text-3xl font-bold tracking-normal text-transparent focus:outline-none focus:ring-2 focus:ring-focus md:text-4xl\"\n                      >\n                        SORA\n                      </NavLink>\n                    </div>\n                    <Spacer y={1} />\n                    <div className=\"flex flex-row items-center justify-center space-x-4\">\n                      <Link as={RemixLink} to=\"/design-system\">\n                        Design 🎨\n                      </Link>\n                      <Link\n                        isExternal\n                        href=\"https://raw.githubusercontent.com/Khanhtran47/Sora/master/LICENSE.txt\"\n                      >\n                        License 📜\n                      </Link>\n                      <Link href=\"#\">Contact ✉️</Link>\n                    </div>\n                    <Spacer y={1} />\n                    <h6 className=\"text-center !text-default-900\">\n                      This site does not store any files on its server. All contents are provided by\n                      non-affiliated third parties.\n                    </h6>\n                  </div>\n                </motion.div>\n              </TabsContent>\n            </AnimatePresence>\n          </Tabs>\n        )}\n      </ClientOnly>\n    </motion.div>\n  );\n};\n\nexport default Settings;\n"
  },
  {
    "path": "app/routes/sitemap[.]xml.tsx",
    "content": "import { generateSitemap } from '@nasa-gcn/remix-seo';\nimport { routes } from '@remix-run/dev/server-build';\nimport type { LoaderFunctionArgs } from '@remix-run/node';\n\nexport function loader({ request }: LoaderFunctionArgs) {\n  return generateSitemap(request, routes, {\n    siteUrl: 'https://sorachill.vercel.app',\n    headers: {\n      'Cache-Control': `public, max-age=${60 * 5}`,\n    },\n  });\n}\n"
  },
  {
    "path": "app/routes/test.tsx",
    "content": "import type { LoaderFunctionArgs } from '@remix-run/node';\n\nimport { redirectWithToast } from '~/utils/server/toast-session.server';\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  return redirectWithToast(request, '/', {\n    title: 'Access Denied',\n    type: 'error',\n    description: \"You don't have permission to access this page.\",\n  });\n};\n\nconst TestPage = () => {\n  return (\n    <div>\n      <h1>Test Page</h1>\n    </div>\n  );\n};\n\nexport default TestPage;\n"
  },
  {
    "path": "app/routes/trending+/$mediaType+/today.tsx",
    "content": "import { json, redirect, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate, useParams } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getTrending } from '~/services/tmdb/tmdb.server';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(({ params }) => {\n  const { mediaType } = params;\n  return [\n    { title: `Sora - Trending ${mediaType} today` },\n    {\n      name: 'keywords',\n      content: `trending, trending ${mediaType}, trending today, trending ${mediaType} today`,\n    },\n    { property: 'og:url', content: `https://sorachill.vercel.app/trending/${mediaType}/today` },\n    { property: 'og:title', content: `Sora - Trending ${mediaType} today` },\n    { name: 'description', content: `Trending ${mediaType} today` },\n    { property: 'og:description', content: `Trending ${mediaType} today` },\n    { name: 'twitter:title', content: `Sora - Trending ${mediaType} today` },\n    { name: 'twitter:description', content: `Trending ${mediaType} today` },\n  ];\n});\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  if (\n    !['all', 'movie', 'tv', 'people'].includes(\n      params.mediaType as 'all' | 'movie' | 'tv' | 'people',\n    )\n  ) {\n    return redirect(`/trending/all/today`);\n  }\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page'));\n  if (!page || page < 1 || page > 1000) page = 1;\n  const todayTrending =\n    params.mediaType === 'people'\n      ? await getTrending('person', 'day', locale, page)\n      : await getTrending(params.mediaType as 'all' | 'movie' | 'tv', 'day', locale, page);\n\n  return json({ todayTrending }, { headers: { 'Cache-Control': CACHE_CONTROL.trending } });\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ params, t }) => {\n    const { mediaType } = params;\n    return (\n      <BreadcrumbItem to={`/trending/${mediaType}/today`} key={`trending-${mediaType}-today`}>\n        {t(`trending.${mediaType}.day`)}\n      </BreadcrumbItem>\n    );\n  },\n  miniTitle: ({ params, t }) => ({\n    title: t(`trending-${params.mediaType}`),\n    subtitle: t('today'),\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst TrendingToday = () => {\n  const { todayTrending } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const location = useLocation();\n  const navigate = useNavigate();\n  const isHydrated = useHydrated();\n  const { mediaType } = useParams();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      return;\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate(`/trending/${mediaType}/week`);\n    }\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ x: '-10%', opacity: 0 }}\n      animate={{ x: '0', opacity: 1 }}\n      exit={{ y: '-10%', opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <MediaList\n        currentPage={todayTrending?.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={todayTrending?.items}\n        itemsType={mediaType === 'all' ? 'movie-tv' : (mediaType as 'movie' | 'tv' | 'people')}\n        listName={t(`trending.${mediaType}.day`)}\n        listType=\"grid\"\n        showListTypeChangeButton={mediaType !== 'people'}\n        totalPages={todayTrending?.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default TrendingToday;\n"
  },
  {
    "path": "app/routes/trending+/$mediaType+/week.tsx",
    "content": "import { json, redirect, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate, useParams } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getTrending } from '~/services/tmdb/tmdb.server';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(({ params }) => {\n  const { mediaType } = params;\n  return [\n    { title: `Sora - Trending ${mediaType} This Week` },\n    {\n      name: 'keywords',\n      content: `trending, trending ${mediaType}, trending this week, trending ${mediaType} this week`,\n    },\n    { property: 'og:url', content: `https://sorachill.vercel.app/trending/${mediaType}/week` },\n    { property: 'og:title', content: `Sora - Trending ${mediaType} This Week` },\n    { name: 'description', content: `Trending ${mediaType} This Week` },\n    { property: 'og:description', content: `Trending ${mediaType} This Week` },\n    { name: 'twitter:title', content: `Sora - Trending ${mediaType} This Week` },\n    { name: 'twitter:description', content: `Trending ${mediaType} This Week` },\n  ];\n});\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  if (\n    !['all', 'movie', 'tv', 'people'].includes(\n      params.mediaType as 'all' | 'movie' | 'tv' | 'people',\n    )\n  ) {\n    return redirect(`/trending/all/week`);\n  }\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page'));\n  if (!page || page < 1 || page > 1000) page = 1;\n  const weekTrending =\n    params.mediaType === 'people'\n      ? await getTrending('person', 'week', locale, page)\n      : await getTrending(params.mediaType as 'all' | 'movie' | 'tv', 'week', locale, page);\n\n  return json({ weekTrending }, { headers: { 'Cache-Control': CACHE_CONTROL.trending } });\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ params, t }) => {\n    const { mediaType } = params;\n    return (\n      <BreadcrumbItem to={`/trending/${mediaType}/week`} key={`trending-${mediaType}-week`}>\n        {t(`trending.${mediaType}.week`)}\n      </BreadcrumbItem>\n    );\n  },\n  miniTitle: ({ params, t }) => ({\n    title: t(`trending-${params.mediaType}`),\n    subtitle: t('week'),\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst TrendingWeek = () => {\n  const { weekTrending } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const location = useLocation();\n  const navigate = useNavigate();\n  const { mediaType } = useParams();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate(`/trending/${mediaType}/today`);\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      return;\n    }\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ x: '-10%', opacity: 0 }}\n      animate={{ x: '0', opacity: 1 }}\n      exit={{ y: '-10%', opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <MediaList\n        currentPage={weekTrending?.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={weekTrending?.items}\n        itemsType={mediaType === 'all' ? 'movie-tv' : (mediaType as 'movie' | 'tv' | 'people')}\n        listName={t(`trending.${mediaType}.week`)}\n        listType=\"grid\"\n        showListTypeChangeButton={mediaType !== 'people'}\n        totalPages={weekTrending?.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default TrendingWeek;\n"
  },
  {
    "path": "app/routes/trending+/$mediaType.tsx",
    "content": "import { type LoaderFunctionArgs } from '@remix-run/node';\nimport { Outlet } from '@remix-run/react';\n\nimport type { Handle } from '~/types/handle';\nimport { redirectWithToast } from '~/utils/server/toast-session.server';\nimport { trendingPages } from '~/constants/tabLinks';\n\nexport const handle: Handle = {\n  showTabLink: true,\n  tabLinkPages: ({ params }) => {\n    const { mediaType } = params;\n    const tabLinkPages = trendingPages[`${mediaType as 'all' | 'movie' | 'tv' | 'people'}`];\n    return tabLinkPages;\n  },\n  tabLinkTo: ({ params }) => `/trending/${params.mediaType}`,\n};\nexport const loader = async ({ params, request }: LoaderFunctionArgs) => {\n  const { mediaType } = params;\n  if (!mediaType || !['all', 'movie', 'tv', 'people'].includes(mediaType))\n    return redirectWithToast(request, `/trending/all/today`, {\n      type: 'error',\n      title: 'Error',\n      description: 'Invalid media type',\n    });\n  return null;\n};\n\nconst TrendingMediaPage = () => <Outlet />;\n\nexport default TrendingMediaPage;\n"
  },
  {
    "path": "app/routes/trending+/_index.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { motion } from 'framer-motion';\nimport { useTranslation } from 'react-i18next';\n\nimport type { IMedia } from '~/types/media';\nimport { getAnimeTrending } from '~/services/consumet/anilist/anilist.server';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getTrending } from '~/services/tmdb/tmdb.server';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page'));\n  if (!page || page < 1 || page > 1000) page = 1;\n\n  const [\n    todayTrending,\n    todayMoviesTrending,\n    todayShowsTrending,\n    todayAnimeTrending,\n    todayPeopleTrending,\n  ] = await Promise.all([\n    getTrending('all', 'day', locale, page),\n    getTrending('movie', 'day', locale, page),\n    getTrending('tv', 'day', locale, page),\n    getAnimeTrending(page, 16),\n    getTrending('person', 'day', locale, page),\n  ]);\n  return json(\n    {\n      todayTrending: todayTrending.items,\n      todayMoviesTrending: todayMoviesTrending.items,\n      todayShowsTrending: todayShowsTrending.items,\n      todayAnimeTrending: todayAnimeTrending?.results as IMedia[],\n      todayPeopleTrending: todayPeopleTrending.items?.map((item) => ({\n        ...item,\n        mediaType: 'people',\n      })) as IMedia[],\n    },\n    { headers: { 'Cache-Control': CACHE_CONTROL.trending } },\n  );\n};\n\nconst TrendingRoute = () => {\n  const {\n    todayTrending,\n    todayMoviesTrending,\n    todayShowsTrending,\n    todayAnimeTrending,\n    todayPeopleTrending,\n  } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const location = useLocation();\n  const navigate = useNavigate();\n  const { t } = useTranslation();\n  const onClickViewMore = (type: 'all' | 'movie' | 'tv' | 'anime' | 'people') => {\n    if (type === 'anime') {\n      navigate(`/anime/trending`);\n    } else {\n      navigate(`/trending/${type}/today`);\n    }\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ x: '-10%', opacity: 0 }}\n      animate={{ x: '0', opacity: 1 }}\n      exit={{ y: '-10%', opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-5\"\n    >\n      <MediaList\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={todayTrending}\n        itemsType=\"movie-tv\"\n        key=\"slider-card-trending-today-all\"\n        listName={t('trending.all.day')}\n        listType=\"slider-card\"\n        navigationButtons\n        onClickViewMore={() => onClickViewMore('all')}\n        showMoreList\n      />\n      <MediaList\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={todayMoviesTrending}\n        itemsType=\"movie\"\n        key=\"slider-card-trending-today-movies\"\n        listName={t('trending.movie.day')}\n        listType=\"slider-card\"\n        navigationButtons\n        onClickViewMore={() => onClickViewMore('movie')}\n        showMoreList\n      />\n      <MediaList\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={todayShowsTrending}\n        itemsType=\"tv\"\n        key=\"slider-card-trending-today-tv\"\n        listName={t('trending.tv.day')}\n        listType=\"slider-card\"\n        navigationButtons\n        onClickViewMore={() => onClickViewMore('tv')}\n        showMoreList\n      />\n      <MediaList\n        items={todayAnimeTrending}\n        itemsType=\"anime\"\n        key=\"slider-card-trending-today-anime\"\n        listName={t('trending-anime')}\n        listType=\"slider-card\"\n        navigationButtons\n        onClickViewMore={() => onClickViewMore('anime')}\n        showMoreList\n      />\n      <MediaList\n        items={todayPeopleTrending}\n        itemsType=\"people\"\n        key=\"slider-card-trending-today-people\"\n        listName={t('trending.people.day')}\n        listType=\"slider-card\"\n        navigationButtons\n        onClickViewMore={() => onClickViewMore('people')}\n        showMoreList\n      />\n    </motion.div>\n  );\n};\n\nexport default TrendingRoute;\n"
  },
  {
    "path": "app/routes/trending.tsx",
    "content": "import { Outlet } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Trending' },\n  { name: 'keywords', content: 'trending, trending movies, trending tv shows, trending anime' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/trending/' },\n  { property: 'og:title', content: 'Sora - Trending' },\n  { name: 'description', content: 'Trending' },\n  { property: 'og:description', content: 'Trending' },\n  { name: 'twitter:title', content: 'Sora - Trending' },\n  { name: 'twitter:description', content: 'Trending' },\n]);\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/trending/\" key=\"trending\">\n      {t('trending.title')}\n    </BreadcrumbItem>\n  ),\n};\n\nconst Trending = () => <Outlet />;\n\nexport default Trending;\n"
  },
  {
    "path": "app/routes/tv-shows+/$tvId+/_index.tsx",
    "content": "import { Card, CardBody } from '@nextui-org/card';\nimport { useMediaQuery } from '@react-hookz/web';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { Link, useLoaderData, useNavigate, useParams } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { useTranslation } from 'react-i18next';\nimport { MimeType } from 'remix-image';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as tvIdLoader } from '~/routes/tv-shows+/$tvId';\nimport { authenticate } from '~/services/supabase';\nimport { getCredits, getRecommendation, getSimilar } from '~/services/tmdb/tmdb.server';\nimport { postFetchDataHandler } from '~/services/tmdb/utils.server';\nimport TMDB from '~/utils/media';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport Image from '~/components/elements/Image';\nimport PhotoIcon from '~/assets/icons/PhotoIcon';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const { tvId } = params;\n  const tid = Number(tvId);\n\n  if (!tid) throw new Response('Not Found', { status: 404 });\n\n  const [similar, credits, recommendations] = await Promise.all([\n    getSimilar('tv', tid),\n    getCredits('tv', tid),\n    getRecommendation('tv', tid),\n  ]);\n\n  if (!similar || !credits || !recommendations) throw new Response('Not Found', { status: 404 });\n\n  return json(\n    {\n      similar,\n      recommendations,\n      topBilledCast: credits &&\n        credits.cast && [...postFetchDataHandler(credits.cast.slice(0, 9), 'people')],\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.detail,\n      },\n    },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader, { 'routes/tv-shows+/$tvId': typeof tvIdLoader }>(\n  ({ matches, params }) => {\n    const tvData = matches.find((match) => match.id === 'routes/tv-shows+/$tvId')?.data;\n    if (!tvData) {\n      return [\n        { title: 'Missing Tv show' },\n        { name: 'description', content: `There is no tv show with ID: ${params.tvId}` },\n      ];\n    }\n    const { detail } = tvData;\n    const { name } = detail || {};\n    return [\n      { title: `Sora - ${name}` },\n      { property: 'og:title', content: `Sora - ${name}` },\n      { property: 'og:url', content: `https://sorachill.vercel.app/tv-shows/${params.tvId}/` },\n      { property: 'twitter:title', content: `Sora - ${name}` },\n    ];\n  },\n);\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/tv-shows/${match.params.tvId}/`}\n      key={`tv-shows-${match.params.tvId}-overview`}\n    >\n      {t('overview')}\n    </BreadcrumbItem>\n  ),\n};\n\nconst TvOverview = () => {\n  const { similar, recommendations, topBilledCast } = useLoaderData<typeof loader>();\n  const tvData = useTypedRouteLoaderData('routes/tv-shows+/$tvId');\n  const rootData = useTypedRouteLoaderData('root');\n  const detail = tvData && tvData.detail;\n  const navigate = useNavigate();\n  const { tvId } = useParams();\n  const { t } = useTranslation();\n\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const onClickViewMore = (type: 'cast' | 'similar' | 'recommendations') => {\n    navigate(`/tv-shows/${detail?.id}/${type}`);\n  };\n  return (\n    <div className=\"mt-3 flex w-full max-w-[1920px] flex-col gap-x-0 gap-y-4 px-3 sm:flex-row sm:items-stretch sm:justify-center sm:gap-x-4 sm:gap-y-0 sm:px-3.5 xl:px-4 2xl:px-5\">\n      <div className=\"flex w-full grow-0 flex-col sm:w-1/3 sm:items-center sm:justify-start\">\n        <div className=\"flex w-full flex-col items-start justify-center gap-y-4 rounded-large bg-content1 p-4 nextui-sm:w-3/4 xl:w-1/2\">\n          <div className=\"flex w-full flex-row items-center justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n            <h6 className=\"grow-0 basis-1/3\">{t('status')}</h6>\n            <p className=\"grow\">{detail?.status}</p>\n          </div>\n          <div className=\"flex w-full flex-row items-start justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n            <h6 className=\"grow-0 basis-1/3\">{t('network')}</h6>\n            <div className=\"flex grow flex-col\">\n              {detail?.networks &&\n                detail.networks.map((network, index) => (\n                  <p key={`network-item-${index}`}>{network?.name}</p>\n                ))}\n            </div>\n          </div>\n          <div className=\"flex w-full flex-row items-center justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n            <h6 className=\"grow-0 basis-1/3\">{t('type')}</h6>\n            <p className=\"grow\">{detail?.type}</p>\n          </div>\n          <div className=\"flex w-full flex-row items-center justify-start gap-x-4 sm:flex-col sm:items-start sm:justify-center\">\n            <h6 className=\"grow-0 basis-1/3\">{t('original-language')}</h6>\n            <p className=\"grow\">\n              {rootData?.languages?.find((lang) => lang.iso_639_1 === detail?.original_language)\n                ?.english_name || detail?.original_language}\n            </p>\n          </div>\n        </div>\n      </div>\n      <div className=\"flex w-full flex-col sm:w-2/3\">\n        <div className=\"flex flex-col items-start justify-start gap-y-4 rounded-large bg-content1 p-4\">\n          <p className=\"text-justify\">{detail?.overview}</p>\n          <div className=\"flex flex-col flex-wrap gap-x-0 gap-y-4 sm:flex-row sm:gap-x-8\">\n            {detail?.created_by && detail?.created_by.length > 0 ? (\n              <div className=\"flex w-full flex-row items-start justify-start gap-x-4 sm:w-fit sm:flex-col\">\n                <h6 className=\"grow-0 basis-1/3 sm:basis-auto\">{t('creators')}</h6>\n                <div className=\"flex grow flex-col\">\n                  {detail.created_by.map((creator) => (\n                    <Link\n                      key={`director-item-${creator.id}}`}\n                      to={`/people/${creator.id}/`}\n                      style={{ lineHeight: '1.75rem' }}\n                      className=\"text-foreground hover:text-primary focus:outline-none focus:ring-2 focus:ring-focus\"\n                    >\n                      {creator.name}\n                    </Link>\n                  ))}\n                </div>\n              </div>\n            ) : null}\n            {detail?.production_countries && detail.production_countries.length > 0 ? (\n              <div className=\"flex w-full flex-row items-start justify-start gap-x-4 sm:w-fit sm:flex-col\">\n                <h6 className=\"grow-0 basis-1/3 sm:basis-auto\">{t('production-countries')}</h6>\n                <div className=\"flex grow flex-col\">\n                  {detail?.production_countries.map((country, index) => (\n                    <p key={`country-item-${index}`}>{country.name}</p>\n                  ))}\n                </div>\n              </div>\n            ) : null}\n            {detail?.spoken_languages && detail.spoken_languages.length > 0 ? (\n              <div className=\"flex w-full flex-row items-start justify-start gap-x-4 sm:w-fit sm:flex-col\">\n                <h6 className=\"grow-0 basis-1/3 sm:basis-auto\">{t('spoken-languages')}</h6>\n                <div className=\"flex grow flex-col\">\n                  {detail?.spoken_languages.map((language, index) => (\n                    <p key={`language-item-${index}`}>{language.english_name}</p>\n                  ))}\n                </div>\n              </div>\n            ) : null}\n          </div>\n        </div>\n        {topBilledCast && topBilledCast.length > 0 ? (\n          <MediaList\n            items={topBilledCast}\n            itemsType=\"people\"\n            key={`tv-top-cast-${tvId}`}\n            listName={t('top-cast')}\n            listType=\"slider-card\"\n            navigationButtons\n            onClickViewMore={() => onClickViewMore('cast')}\n            showMoreList\n          />\n        ) : null}\n        {detail?.seasons && detail?.seasons.length > 0 ? (\n          <>\n            <h2 style={{ margin: '20px 0 5px 0' }}>{t('seasons')}</h2>\n            <div className=\"flex w-full flex-col gap-4\">\n              {detail.seasons\n                .filter((season) => !season.name?.includes('Specials'))\n                .map((season) => (\n                  <Card\n                    role=\"button\"\n                    key={season.id}\n                    isHoverable\n                    isPressable\n                    className=\"data-[hover=true]:ring-2 data-[hover=true]:ring-focus\"\n                    onPress={() =>\n                      navigate(`/tv-shows/${detail.id}/season/${season.season_number}/`)\n                    }\n                  >\n                    <CardBody className=\"flex flex-row flex-nowrap justify-start p-0\">\n                      {season.poster_path ? (\n                        <Image\n                          src={TMDB.posterUrl(season?.poster_path, 'w154')}\n                          width=\"130px\"\n                          height=\"100%\"\n                          classNames={{\n                            wrapper: 'z-0 aspect-[2/3] max-h-[195px] min-w-[130px]',\n                            img: 'm-0 max-h-[193px]',\n                          }}\n                          alt={season.name}\n                          title={season.name}\n                          placeholder=\"empty\"\n                          options={{\n                            contentType: MimeType.WEBP,\n                          }}\n                          responsive={[\n                            {\n                              size: {\n                                width: 130,\n                                height: 195,\n                              },\n                            },\n                          ]}\n                        />\n                      ) : (\n                        <div className=\"z-0 flex  aspect-[2/3] max-h-[193px] min-w-[130px] items-center justify-center rounded-large bg-default\">\n                          <PhotoIcon width={36} height={36} />\n                        </div>\n                      )}\n                      <div className=\"flex flex-col justify-start p-5\">\n                        <h4>{season.name}</h4>\n                        <h5>\n                          {season.air_date} | {season.episode_count} {t('episodes')}\n                        </h5>\n                        {!isSm ? <h6 className=\"!line-clamp-3\">{season.overview}</h6> : null}\n                      </div>\n                    </CardBody>\n                  </Card>\n                ))}\n            </div>\n          </>\n        ) : null}\n        {recommendations && recommendations.items && recommendations.items.length > 0 ? (\n          <MediaList\n            genresMovie={rootData?.genresMovie}\n            genresTv={rootData?.genresTv}\n            items={recommendations.items}\n            itemsType=\"tv\"\n            key={`tv-recommendations-${tvId}`}\n            listName={t('recommendations')}\n            listType=\"slider-card\"\n            navigationButtons\n            onClickViewMore={() => onClickViewMore('recommendations')}\n            showMoreList\n          />\n        ) : null}\n        {similar.items && similar.items.length > 0 ? (\n          <MediaList\n            genresMovie={rootData?.genresMovie}\n            genresTv={rootData?.genresTv}\n            items={similar.items}\n            itemsType=\"tv\"\n            key={`tv-similar-${tvId}`}\n            listName={t('similar-tv-shows')}\n            listType=\"slider-card\"\n            navigationButtons\n            onClickViewMore={() => onClickViewMore('similar')}\n            showMoreList\n          />\n        ) : null}\n      </div>\n    </div>\n  );\n};\n\nexport default TvOverview;\n"
  },
  {
    "path": "app/routes/tv-shows+/$tvId+/cast.tsx",
    "content": "import { useRef } from 'react';\nimport { Pagination } from '@nextui-org/pagination';\nimport { useMediaQuery } from '@react-hookz/web';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as tvIdLoader } from '~/routes/tv-shows+/$tvId';\nimport { authenticate } from '~/services/supabase';\nimport { getCredits } from '~/services/tmdb/tmdb.server';\nimport type { ITvShowDetail } from '~/services/tmdb/tmdb.types';\nimport { postFetchDataHandler } from '~/services/tmdb/utils.server';\nimport TMDB from '~/utils/media';\nimport useSplitArrayIntoPage from '~/utils/react/hooks/useSplitArrayIntoPage';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const { tvId } = params;\n  const mid = Number(tvId);\n\n  if (!mid) throw new Response('Not found', { status: 404 });\n  const credits = await getCredits('tv', mid);\n\n  if (!credits) throw new Response('Not found', { status: 404 });\n\n  return json(\n    { cast: [...postFetchDataHandler(credits.cast, 'people')] },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.detail },\n    },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader, { 'routes/tv-shows+/$tvId': typeof tvIdLoader }>(\n  ({ matches, params }) => {\n    const tvData = matches.find((match) => match.id === 'routes/tv-shows+/$tvId')?.data;\n    if (!tvData) {\n      return [\n        { title: 'Missing Tv show' },\n        { name: 'description', content: `There is no tv show with ID: ${params.tvId}` },\n      ];\n    }\n    const { detail } = tvData;\n    const { name } = detail || {};\n    return [\n      { title: `Sora - ${name} - Cast` },\n      { property: 'og:title', content: `Sora - ${name} - Cast` },\n      { property: 'og:url', content: `https://sorachill.vercel.app/tv-shows/${params.tvId}/cast` },\n      { property: 'twitter:title', content: `Sora - ${name} - Cast` },\n    ];\n  },\n);\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/tv-shows/${match.params.tvId}/cast`}\n      key={`tv-shows-${match.params.tvId}-cast`}\n    >\n      {t('cast')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch ? (parentMatch?.data as { detail: ITvShowDetail })?.detail?.name || '' : '',\n    subtitle: t('cast'),\n    showImage: parentMatch\n      ? (parentMatch?.data as { detail: ITvShowDetail })?.detail?.poster_path !== undefined\n      : false,\n    imageUrl: TMDB?.posterUrl(\n      parentMatch\n        ? (parentMatch?.data as { detail: ITvShowDetail })?.detail?.poster_path || ''\n        : '',\n      'w92',\n    ),\n  }),\n};\n\nconst TvCastPage = () => {\n  const { cast } = useLoaderData<typeof loader>();\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const ref = useRef<HTMLDivElement>(null);\n  const { gotoPage, currentPage, maxPage, currentData } = useSplitArrayIntoPage(cast || [], 20);\n\n  return (\n    <div className=\"mt-3 flex w-full max-w-[1920px] flex-col gap-y-4 px-3 sm:px-3.5 xl:px-4 2xl:px-5\">\n      <div ref={ref} />\n      <MediaList items={currentData} itemsType=\"people\" listType=\"grid\" />\n      {maxPage > 1 ? (\n        <div className=\"mt-7 flex flex-row justify-center\">\n          <Pagination\n            // showControls={!isSm}\n            total={maxPage}\n            initialPage={currentPage}\n            // shadow\n            onChange={(page) => {\n              gotoPage(page);\n              ref.current?.scrollIntoView({\n                behavior: 'instant',\n                block: 'center',\n                inline: 'nearest',\n              });\n            }}\n            {...(isSm && { size: 'sm' })}\n          />\n        </div>\n      ) : null}\n    </div>\n  );\n};\n\nexport default TvCastPage;\n"
  },
  {
    "path": "app/routes/tv-shows+/$tvId+/crew.tsx",
    "content": "import { useRef } from 'react';\nimport { Pagination } from '@nextui-org/pagination';\nimport { useMediaQuery } from '@react-hookz/web';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as tvIdLoader } from '~/routes/tv-shows+/$tvId';\nimport { authenticate } from '~/services/supabase';\nimport { getCredits } from '~/services/tmdb/tmdb.server';\nimport type { ITvShowDetail } from '~/services/tmdb/tmdb.types';\nimport { postFetchDataHandler } from '~/services/tmdb/utils.server';\nimport TMDB from '~/utils/media';\nimport useSplitArrayIntoPage from '~/utils/react/hooks/useSplitArrayIntoPage';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const { tvId } = params;\n  const mid = Number(tvId);\n\n  if (!mid) throw new Response('Not found', { status: 404 });\n  const credits = await getCredits('tv', mid);\n\n  if (!credits) throw new Response('Not found', { status: 404 });\n\n  return json(\n    { crew: [...postFetchDataHandler(credits.crew, 'people')] },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.detail },\n    },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader, { 'routes/tv-shows+/$tvId': typeof tvIdLoader }>(\n  ({ matches, params }) => {\n    const tvData = matches.find((match) => match.id === 'routes/tv-shows+/$tvId')?.data;\n    if (!tvData) {\n      return [\n        { title: 'Missing Tv show' },\n        { name: 'description', content: `There is no tv show with ID: ${params.tvId}` },\n      ];\n    }\n    const { detail } = tvData;\n    const { name } = detail || {};\n    return [\n      { title: `Sora - ${name} - Crew` },\n      { property: 'og:title', content: `Sora - ${name} - Crew` },\n      { property: 'og:url', content: `https://sorachill.vercel.app/tv-shows/${params.tvId}/crew` },\n      { property: 'twitter:title', content: `Sora - ${name} - Crew` },\n    ];\n  },\n);\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/tv-shows/${match.params.tvId}/crew`}\n      key={`tv-shows-${match.params.tvId}-crew`}\n    >\n      {t('crew')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch ? (parentMatch?.data as { detail: ITvShowDetail })?.detail?.name || '' : '',\n    subtitle: t('crew'),\n    showImage: parentMatch\n      ? (parentMatch?.data as { detail: ITvShowDetail })?.detail?.poster_path !== undefined\n      : false,\n    imageUrl: TMDB?.posterUrl(\n      parentMatch\n        ? (parentMatch?.data as { detail: ITvShowDetail })?.detail?.poster_path || ''\n        : '',\n      'w92',\n    ),\n  }),\n};\n\nconst TvCrewPage = () => {\n  const { crew } = useLoaderData<typeof loader>();\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const ref = useRef<HTMLDivElement>(null);\n  const { gotoPage, currentPage, maxPage, currentData } = useSplitArrayIntoPage(crew || [], 20);\n\n  return (\n    <div className=\"mt-3 flex w-full max-w-[1920px] flex-col gap-y-4 px-3 sm:px-3.5 xl:px-4 2xl:px-5\">\n      <div ref={ref} />\n      <MediaList items={currentData} itemsType=\"people\" listType=\"grid\" />\n      {maxPage > 1 ? (\n        <div className=\"mt-7 flex flex-row justify-center\">\n          <Pagination\n            // showControls={!isSm}\n            total={maxPage}\n            initialPage={currentPage}\n            // shadow\n            onChange={(page) => {\n              gotoPage(page);\n              ref.current?.scrollIntoView({\n                behavior: 'instant',\n                block: 'center',\n                inline: 'nearest',\n              });\n            }}\n            {...(isSm && { size: 'sm' })}\n          />\n        </div>\n      ) : null}\n    </div>\n  );\n};\n\nexport default TvCrewPage;\n"
  },
  {
    "path": "app/routes/tv-shows+/$tvId+/photos.tsx",
    "content": "import * as React from 'react';\nimport { Spacer } from '@nextui-org/spacer';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { useTranslation } from 'react-i18next';\nimport { Gallery, Item, type GalleryProps } from 'react-photoswipe-gallery';\nimport { MimeType } from 'remix-image';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as tvIdLoader } from '~/routes/tv-shows+/$tvId';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getImages } from '~/services/tmdb/tmdb.server';\nimport type { ITvShowDetail } from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport Image from '~/components/elements/Image';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const { tvId } = params;\n  const mid = Number(tvId);\n  if (!mid) throw new Response('Not Found', { status: 404 });\n\n  const images = await getImages('tv', mid, locale);\n  if (!images) throw new Response('Not Found', { status: 404 });\n\n  return json(\n    { images },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.detail },\n    },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader, { 'routes/tv-shows+/$tvId': typeof tvIdLoader }>(\n  ({ matches, params }) => {\n    const tvData = matches.find((match) => match.id === 'routes/tv-shows+/$tvId')?.data;\n    if (!tvData) {\n      return [\n        { title: 'Missing Tv show' },\n        { name: 'description', content: `There is no tv show with ID: ${params.tvId}` },\n      ];\n    }\n    const { detail } = tvData;\n    const { name } = detail || {};\n    return [\n      { title: `Sora - ${name} - Photos` },\n      { property: 'og:title', content: `Sora - ${name} - Photos` },\n      {\n        property: 'og:url',\n        content: `https://sorachill.vercel.app/tv-shows/${params.tvId}/photos`,\n      },\n      { property: 'twitter:title', content: `Sora - ${name} - Photos` },\n    ];\n  },\n);\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/tv-shows/${match.params.tvId}/photos`}\n      key={`tv-shows-${match.params.tvId}-photos`}\n    >\n      {t('photos')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch ? (parentMatch?.data as { detail: ITvShowDetail })?.detail?.name || '' : '',\n    subtitle: t('photos'),\n    showImage: parentMatch\n      ? (parentMatch?.data as { detail: ITvShowDetail })?.detail?.poster_path !== undefined\n      : false,\n    imageUrl: TMDB?.posterUrl(\n      parentMatch\n        ? (parentMatch?.data as { detail: ITvShowDetail })?.detail?.poster_path || ''\n        : '',\n      'w92',\n    ),\n  }),\n};\n\nconst uiElements: GalleryProps['uiElements'] = [\n  {\n    name: 'custom-rotate-button',\n    ariaLabel: 'Rotate',\n    order: 9,\n    isButton: true,\n    html: {\n      isCustomSVG: true,\n      inner:\n        '<path d=\"M13.887 6.078C14.258 6.234 14.5 6.598 14.5 7V8.517C18.332 8.657 21.258 10.055 23.15 12.367 24.519 14.041 25.289 16.13 25.496 18.409A1 1 0 0123.504 18.591C23.327 16.645 22.68 14.952 21.601 13.633 20.156 11.867 17.831 10.653 14.5 10.517V12A1.002 1.002 0 0112.779 12.693L10.304 10.121A1.002 1.002 0 0110.324 8.713L12.8 6.286A1 1 0 0113.887 6.078ZM7.5 16A1.5 1.5 0 006 17.5V24.5A1.5 1.5 0 007.5 26H17.5A1.5 1.5 0 0019 24.5V17.5A1.5 1.5 0 0017.5 16H7.5Z\" id=\"pswp__icn-rotate\"/>',\n      outlineID: 'pswp__icn-rotate',\n    },\n    appendTo: 'bar',\n    onClick: (_, __, pswpInstance) => {\n      const item = pswpInstance.currSlide?.content.element;\n\n      const prevRotateAngle = Number(item?.dataset.rotateAngel) || 0;\n      const rotateAngle = prevRotateAngle === 270 ? 0 : prevRotateAngle + 90;\n\n      // add slide rotation\n      if (item) {\n        item.style.transform = `${item.style.transform?.replace(\n          `rotate(-${prevRotateAngle}deg)`,\n          '',\n        )} rotate(-${rotateAngle}deg)`;\n        item.dataset.rotateAngel = String(rotateAngle);\n      }\n    },\n    onInit: (_, pswpInstance) => {\n      // remove applied rotation on slide change\n      // https://photoswipe.com/events/#slide-content-events\n      pswpInstance.on('contentRemove', () => {\n        const item = pswpInstance.currSlide?.content.element;\n        if (item) {\n          item.style.transform = `${item.style.transform?.replace(\n            `rotate(-${item.dataset.rotateAngel || 0}deg)`,\n            '',\n          )}`;\n          delete item.dataset.rotateAngel;\n        }\n      });\n    },\n  },\n];\n\nconst TvPhotosPage = () => {\n  const { images } = useLoaderData<typeof loader>();\n  const { t } = useTranslation();\n  const tvData = useTypedRouteLoaderData('routes/tv-shows+/$tvId');\n\n  return (\n    <div className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\">\n      <Spacer y={5} />\n      {images?.backdrops && images.backdrops.length > 0 && (\n        <>\n          <h5 className=\"flex w-full justify-center\">\n            <strong>{t('backdrops')}</strong>\n          </h5>\n          <Spacer y={2.5} />\n          <Gallery withCaption withDownloadButton uiElements={uiElements}>\n            <div className=\"grid grid-cols-1 justify-center gap-3 xs:grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6\">\n              {images?.backdrops?.map((image) => (\n                <Item\n                  key={image.file_path}\n                  cropped\n                  original={TMDB.profileUrl(image?.file_path, 'original')}\n                  thumbnail={TMDB.profileUrl(image?.file_path, 'w185')}\n                  alt={`Backdrop of ${tvData?.detail?.name} image size ${image.width}x${image.height}`}\n                  caption={`Backdrop of ${tvData?.detail?.name} size ${image.width}x${image.height}`}\n                  width={image.width}\n                  height={image.height}\n                >\n                  {({ ref, open }) => (\n                    <Image\n                      src={TMDB.profileUrl(image?.file_path, 'w185')}\n                      ref={ref as React.MutableRefObject<HTMLImageElement>}\n                      onClick={open}\n                      alt={`Backdrop of ${tvData?.detail?.name} image size ${image.width}x${image.height}`}\n                      radius=\"lg\"\n                      classNames={{\n                        img: 'h-auto min-w-[120px] cursor-pointer object-cover 2xs:min-w-[185px]',\n                      }}\n                      title={tvData?.detail?.name}\n                      placeholder=\"empty\"\n                      options={{\n                        contentType: MimeType.WEBP,\n                      }}\n                    />\n                  )}\n                </Item>\n              ))}\n            </div>\n          </Gallery>\n          <Spacer y={5} />\n        </>\n      )}\n      {images?.logos && images.logos.length > 0 && (\n        <>\n          <h5 className=\"flex w-full justify-center\">\n            <strong>{t('logos')}</strong>\n          </h5>\n          <Spacer y={2.5} />\n          <Gallery withCaption withDownloadButton uiElements={uiElements}>\n            <div className=\"grid grid-cols-1 justify-center gap-3 xs:grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6\">\n              {images?.logos?.map((image) => (\n                <Item\n                  key={image.file_path}\n                  cropped\n                  original={TMDB.logoUrl(image?.file_path, 'original')}\n                  thumbnail={TMDB.logoUrl(image?.file_path, 'w185')}\n                  alt={`Logo of ${tvData?.detail?.name} image size ${image.width}x${image.height}`}\n                  caption={`Logo of ${tvData?.detail?.name} size ${image.width}x${image.height}`}\n                  width={image.width}\n                  height={image.height}\n                >\n                  {({ ref, open }) => (\n                    <Image\n                      src={TMDB.logoUrl(image?.file_path, 'w185')}\n                      ref={ref as React.MutableRefObject<HTMLImageElement>}\n                      onClick={open}\n                      alt={`Logo of ${tvData?.detail?.name} image size ${image.width}x${image.height}`}\n                      radius=\"lg\"\n                      classNames={{\n                        img: 'h-auto min-w-[120px] cursor-pointer object-cover 2xs:min-w-[185px]',\n                      }}\n                      title={tvData?.detail?.name}\n                      placeholder=\"empty\"\n                      options={{\n                        contentType: MimeType.WEBP,\n                      }}\n                    />\n                  )}\n                </Item>\n              ))}\n            </div>\n          </Gallery>\n          <Spacer y={5} />\n        </>\n      )}\n      {images?.posters && images.posters.length > 0 && (\n        <>\n          <h5 className=\"flex w-full justify-center\">\n            <strong>{t('posters')}</strong>\n          </h5>\n          <Spacer y={2.5} />\n          <Gallery withCaption withDownloadButton uiElements={uiElements}>\n            <div className=\"grid grid-cols-1 justify-center gap-3 xs:grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6\">\n              {images?.posters?.map((image) => (\n                <Item\n                  key={image.file_path}\n                  cropped\n                  original={TMDB.profileUrl(image?.file_path, 'original')}\n                  thumbnail={TMDB.profileUrl(image?.file_path, 'w185')}\n                  alt={`Poster of ${tvData?.detail?.name} image size ${image.width}x${image.height}`}\n                  caption={`Poster of ${tvData?.detail?.name} size ${image.width}x${image.height}`}\n                  width={image.width}\n                  height={image.height}\n                >\n                  {({ ref, open }) => (\n                    <Image\n                      src={TMDB.profileUrl(image?.file_path, 'w185')}\n                      ref={ref as React.MutableRefObject<HTMLImageElement>}\n                      onClick={open}\n                      alt={`Poster of ${tvData?.detail?.name} image size ${image.width}x${image.height}`}\n                      radius=\"lg\"\n                      classNames={{\n                        img: 'h-auto min-w-[120px] cursor-pointer object-cover 2xs:min-w-[185px]',\n                      }}\n                      title={tvData?.detail?.name}\n                      placeholder=\"empty\"\n                      options={{\n                        contentType: MimeType.WEBP,\n                      }}\n                    />\n                  )}\n                </Item>\n              ))}\n            </div>\n          </Gallery>\n        </>\n      )}\n    </div>\n  );\n};\n\nexport default TvPhotosPage;\n"
  },
  {
    "path": "app/routes/tv-shows+/$tvId+/recommendations.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as tvIdLoader } from '~/routes/tv-shows+/$tvId';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getRecommendation } from '~/services/tmdb/tmdb.server';\nimport type { ITvShowDetail } from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const { tvId } = params;\n  const mid = Number(tvId);\n  if (!mid) throw new Response('Not Found', { status: 404 });\n\n  const locale = await i18next.getLocale(request);\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page')) || undefined;\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  const recommendations = await getRecommendation('tv', mid, page, locale);\n  if (!recommendations) throw new Response('Not Found', { status: 404 });\n\n  return json(\n    {\n      recommendations,\n    },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.detail },\n    },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader, { 'routes/tv-shows+/$tvId': typeof tvIdLoader }>(\n  ({ matches, params }) => {\n    const tvData = matches.find((match) => match.id === 'routes/tv-shows+/$tvId')?.data;\n    if (!tvData) {\n      return [\n        { title: 'Missing Tv show' },\n        { name: 'description', content: `There is no tv show with ID: ${params.tvId}` },\n      ];\n    }\n    const { detail } = tvData;\n    const { name } = detail || {};\n    return [\n      { title: `Sora - ${name} - Recommendations` },\n      { property: 'og:title', content: `Sora - ${name} - Recommendations` },\n      {\n        property: 'og:url',\n        content: `https://sorachill.vercel.app/tv-shows/${params.tvId}/recommendations`,\n      },\n      { property: 'twitter:title', content: `Sora - ${name} - Recommendations` },\n    ];\n  },\n);\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/tv-shows/${match.params.tvId}/recommendations`}\n      key={`tv-shows-${match.params.tvId}-recommendations`}\n    >\n      {t('recommendations')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch ? (parentMatch?.data as { detail: ITvShowDetail })?.detail?.name || '' : '',\n    subtitle: t('recommendations'),\n    showImage: parentMatch\n      ? (parentMatch?.data as { detail: ITvShowDetail })?.detail?.poster_path !== undefined\n      : false,\n    imageUrl: TMDB?.posterUrl(\n      parentMatch\n        ? (parentMatch?.data as { detail: ITvShowDetail })?.detail?.poster_path || ''\n        : '',\n      'w92',\n    ),\n  }),\n  showListViewChangeButton: true,\n};\n\nconst TvRecommendationsPage = () => {\n  const { recommendations } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const { t } = useTranslation();\n\n  return (\n    <div className=\"mt-3 flex w-full max-w-[1920px] flex-col gap-y-4 px-3 sm:px-3.5 xl:px-4 2xl:px-5\">\n      <MediaList\n        currentPage={recommendations?.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={recommendations?.items}\n        itemsType=\"tv\"\n        listName={t('recommendations')}\n        listType=\"grid\"\n        scrollToTopListAfterChangePage\n        showListTypeChangeButton\n        totalPages={recommendations?.totalPages}\n      />\n    </div>\n  );\n};\n\nexport default TvRecommendationsPage;\n"
  },
  {
    "path": "app/routes/tv-shows+/$tvId+/similar.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as tvIdLoader } from '~/routes/tv-shows+/$tvId';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getSimilar } from '~/services/tmdb/tmdb.server';\nimport type { ITvShowDetail } from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const { tvId } = params;\n  const mid = Number(tvId);\n  if (!mid) throw new Response('Not Found', { status: 404 });\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page')) || undefined;\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  const similar = await getSimilar('tv', mid, page, locale);\n  if (!similar) throw new Response('Not Found', { status: 404 });\n\n  return json(\n    {\n      similar,\n    },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.detail },\n    },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader, { 'routes/tv-shows+/$tvId': typeof tvIdLoader }>(\n  ({ matches, params }) => {\n    const tvData = matches.find((match) => match.id === 'routes/tv-shows+/$tvId')?.data;\n    if (!tvData) {\n      return [\n        { title: 'Missing Tv show' },\n        { name: 'description', content: `There is no tv show with ID: ${params.tvId}` },\n      ];\n    }\n    const { detail } = tvData;\n    const { name } = detail || {};\n    return [\n      { title: `Sora - ${name} - Similar` },\n      { property: 'og:title', content: `Sora - ${name} - Similar` },\n      {\n        property: 'og:url',\n        content: `https://sorachill.vercel.app/tv-shows/${params.tvId}/similar`,\n      },\n      { property: 'twitter:title', content: `Sora - ${name} - Similar` },\n    ];\n  },\n);\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/tv-shows/${match.params.tvId}/similar`}\n      key={`tv-shows-${match.params.tvId}-similar`}\n    >\n      {t('similar')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch ? (parentMatch?.data as { detail: ITvShowDetail })?.detail?.name || '' : '',\n    subtitle: t('similar'),\n    showImage: parentMatch\n      ? (parentMatch?.data as { detail: ITvShowDetail })?.detail?.poster_path !== undefined\n      : false,\n    imageUrl: TMDB?.posterUrl(\n      parentMatch\n        ? (parentMatch?.data as { detail: ITvShowDetail })?.detail?.poster_path || ''\n        : '',\n      'w92',\n    ),\n  }),\n  showListViewChangeButton: true,\n};\n\nconst TvSimilarPage = () => {\n  const { similar } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const { t } = useTranslation();\n\n  return (\n    <div className=\"mt-3 flex w-full max-w-[1920px] flex-col gap-y-4 px-3 sm:px-3.5 xl:px-4 2xl:px-5\">\n      <MediaList\n        currentPage={similar?.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={similar?.items}\n        itemsType=\"tv\"\n        listName={t('similar-tv-shows')}\n        listType=\"grid\"\n        scrollToTopListAfterChangePage\n        showListTypeChangeButton\n        totalPages={similar?.totalPages}\n      />\n    </div>\n  );\n};\n\nexport default TvSimilarPage;\n"
  },
  {
    "path": "app/routes/tv-shows+/$tvId+/videos.tsx",
    "content": "import { useEffect, useRef, useState } from 'react';\nimport { Card, CardBody, CardFooter } from '@nextui-org/card';\nimport { useMediaQuery } from '@react-hookz/web';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useFetcher, useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { AnimatePresence, motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\nimport { MimeType } from 'remix-image';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as tvIdLoader } from '~/routes/tv-shows+/$tvId';\nimport { authenticate } from '~/services/supabase';\nimport { getVideos } from '~/services/tmdb/tmdb.server';\nimport type { ITvShowDetail } from '~/services/tmdb/tmdb.types';\nimport type { IYoutubeItem } from '~/services/youtube/youtube.types';\nimport TMDB from '~/utils/media';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport { Dialog, DialogContent, DialogTrigger } from '~/components/elements/Dialog';\nimport WatchTrailer, { type Trailer } from '~/components/elements/dialog/WatchTrailerDialog';\nimport Image from '~/components/elements/Image';\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '~/components/elements/tab/Tabs';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const { tvId } = params;\n  const tid = Number(tvId);\n\n  if (!tid) throw new Response('Not Found', { status: 404 });\n  const videos = await getVideos('tv', tid);\n\n  return json(\n    { videos },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.detail },\n    },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader, { 'routes/tv-shows+/$tvId': typeof tvIdLoader }>(\n  ({ matches, params }) => {\n    const tvData = matches.find((match) => match.id === 'routes/tv-shows+/$tvId')?.data;\n    if (!tvData) {\n      return [\n        { title: 'Missing Tv show' },\n        { name: 'description', content: `There is no tv show with ID: ${params.tvId}` },\n      ];\n    }\n    const { detail } = tvData;\n    const { name } = detail || {};\n    return [\n      { title: `Sora - ${name} - Videos` },\n      { property: 'og:title', content: `Sora - ${name} - Videos` },\n      {\n        property: 'og:url',\n        content: `https://sorachill.vercel.app/tv-shows/${params.tvId}/videos`,\n      },\n      { property: 'twitter:title', content: `Sora - ${name} - Videos` },\n    ];\n  },\n);\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/tv-shows/${match.params.tvId}/videos`}\n      key={`tv-shows-${match.params.tvId}-videos`}\n    >\n      {t('videos')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch ? (parentMatch?.data as { detail: ITvShowDetail })?.detail?.name || '' : '',\n    subtitle: t('videos'),\n    showImage: parentMatch\n      ? (parentMatch?.data as { detail: ITvShowDetail })?.detail?.poster_path !== undefined\n      : false,\n    imageUrl: TMDB?.posterUrl(\n      parentMatch\n        ? (parentMatch?.data as { detail: ITvShowDetail })?.detail?.poster_path || ''\n        : '',\n      'w92',\n    ),\n  }),\n};\n\nconst TvVideosPage = () => {\n  const { videos } = useLoaderData<typeof loader>();\n  const fetcher = useFetcher<{ youtubeVideo: IYoutubeItem[] }>();\n  const { t } = useTranslation();\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const [activeType, setActiveType] = useState('trailer');\n  const [activeTypeVideos, setActiveTypeVideos] = useState<IYoutubeItem[] | []>([]);\n  const [visible, setVisible] = useState(false);\n  const [trailer, setTrailer] = useState<Trailer>({});\n  const underlineRef = useRef<HTMLDivElement>(null);\n  const typesVideo = [\n    {\n      id: 'trailer',\n      activeVideo: 'trailer',\n    },\n    {\n      id: 'teaser',\n      activeVideo: 'teaser',\n    },\n    {\n      id: 'clip',\n      activeVideo: 'clip',\n    },\n    {\n      id: 'behind_the_scenes',\n      activeVideo: 'behind-the-scenes',\n    },\n    {\n      id: 'bloopers',\n      activeVideo: 'bloopers',\n    },\n    {\n      id: 'featurette',\n      activeVideo: 'featurette',\n    },\n    {\n      id: 'opening_credits',\n      activeVideo: 'opening-credits',\n    },\n  ];\n  useEffect(() => {\n    if (videos) {\n      let activeVideo = [];\n      const activeTypeVideo = typesVideo.find((item) => item.id === activeType);\n      activeVideo = videos.results?.filter((video) => video.type === activeTypeVideo?.activeVideo);\n      const keyVideo = activeVideo.map((item) => item.key).join(',');\n      if (keyVideo) fetcher.load(`/api/youtube-video?id=${keyVideo}`);\n      else setActiveTypeVideos([]);\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [activeType, videos]);\n  useEffect(() => {\n    if (fetcher.data && fetcher.data.youtubeVideo) {\n      // @ts-expect-error\n      setActiveTypeVideos(fetcher.data.youtubeVideo);\n    }\n  }, [fetcher.data]);\n\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  const handleDragEnd = (event: MouseEvent | PointerEvent | TouchEvent, info: PanInfo) => {\n    const currentTab = typesVideo.find((type) => type.id === activeType);\n    if (info.offset?.x > 100) {\n      // swipe right\n      if (currentTab?.id === 'trailer') {\n        setActiveType('opening_credits');\n      } else {\n        const index = typesVideo.findIndex((type) => type.id === activeType);\n        setActiveType(typesVideo[index - 1].id);\n      }\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      // swipe left\n      if (currentTab?.id === 'opening_credits') {\n        setActiveType('trailer');\n      } else {\n        const index = typesVideo.findIndex((type) => type.id === activeType);\n        setActiveType(typesVideo[index + 1].id);\n      }\n    }\n  };\n\n  return (\n    <div className=\"mt-3 flex w-full max-w-[1920px] flex-col gap-x-0 gap-y-4 px-3 sm:flex-row sm:items-stretch sm:justify-center sm:gap-x-4 sm:gap-y-0 sm:px-3.5 xl:px-4 2xl:px-5\">\n      <Tabs\n        defaultValue={activeType}\n        value={activeType}\n        orientation={isSm ? 'horizontal' : 'vertical'}\n        onValueChange={(value) => setActiveType(value)}\n        className=\"w-full\"\n      >\n        <TabsList>\n          {typesVideo.map((type) => (\n            <TabsTrigger key={type.id} value={type.id} className=\"relative\">\n              <h6 className=\"!m-0\">{t(type.activeVideo)}</h6>\n              {activeType === type.id ? (\n                <motion.div\n                  className=\"absolute overflow-hidden rounded-small bg-default-foreground data-[orientation=horizontal]:bottom-0 data-[orientation=vertical]:left-0 data-[orientation=horizontal]:h-1 data-[orientation=vertical]:h-1/2 data-[orientation=horizontal]:w-1/2 data-[orientation=vertical]:w-1\"\n                  layoutId=\"video-underline\"\n                  data-orientation={isSm ? 'horizontal' : 'vertical'}\n                  ref={underlineRef}\n                />\n              ) : null}\n            </TabsTrigger>\n          ))}\n        </TabsList>\n        <Dialog open={visible} onOpenChange={setVisible}>\n          <AnimatePresence mode=\"wait\">\n            <TabsContent value={activeType}>\n              <motion.div\n                initial={{ opacity: 0 }}\n                animate={{ opacity: 1 }}\n                exit={{ opacity: 0 }}\n                transition={{ ease: 'easeInOut', duration: 0.3 }}\n                drag={isMobile ? 'x' : false}\n                dragConstraints={{ left: 0, right: 0 }}\n                dragElastic={0.4}\n                onDragEnd={handleDragEnd}\n                dragDirectionLock\n              >\n                <div className=\"grid w-full grid-cols-1 justify-items-center gap-4 lg:grid-cols-2 3xl:grid-cols-3 4xl:grid-cols-4\">\n                  {activeTypeVideos\n                    ? activeTypeVideos.map((video) => (\n                        <DialogTrigger asChild key={video?.id}>\n                          <Card\n                            isPressable\n                            isHoverable\n                            role=\"figure\"\n                            className=\"w-[320px] data-[hover=true]:ring-2 data-[hover=true]:ring-focus\"\n                            onPress={() => {\n                              const videoPlay = videos?.results?.find(\n                                (item) => item.key === video.id,\n                              );\n                              if (videoPlay) {\n                                setTrailer(videoPlay);\n                              }\n                            }}\n                          >\n                            <CardBody className=\"shrink-0 grow-0 overflow-hidden p-0\">\n                              <Image\n                                src={video?.snippet?.thumbnails?.medium?.url}\n                                width={320}\n                                height={180}\n                                alt={video?.snippet?.title}\n                                loading=\"lazy\"\n                                title={video?.snippet?.title}\n                                placeholder=\"empty\"\n                                options={{ contentType: MimeType.WEBP }}\n                                responsive={[{ size: { width: 320, height: 180 } }]}\n                              />\n                            </CardBody>\n                            <CardFooter className=\"flex flex-col items-start justify-start\">\n                              <h6 className=\"!m-0 text-left font-semibold\">\n                                {video?.snippet?.title}\n                              </h6>\n                              <p className=\"opacity-70\">{video?.snippet?.channelTitle}</p>\n                            </CardFooter>\n                          </Card>\n                        </DialogTrigger>\n                      ))\n                    : null}\n                </div>\n              </motion.div>\n            </TabsContent>\n          </AnimatePresence>\n          <DialogContent className=\"overflow-hidden !p-0\">\n            <WatchTrailer trailer={trailer} />\n          </DialogContent>\n        </Dialog>\n      </Tabs>\n    </div>\n  );\n};\n\nexport default TvVideosPage;\n"
  },
  {
    "path": "app/routes/tv-shows+/$tvId.tsx",
    "content": "import { useEffect, useRef } from 'react';\nimport { useIntersectionObserver } from '@react-hookz/web';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { Outlet, useLoaderData, useLocation } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, useTransform } from 'framer-motion';\nimport Vibrant from 'node-vibrant';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getImdbRating, getTvShowDetail, getTvShowIMDBId } from '~/services/tmdb/tmdb.server';\nimport type { ITvShowDetail } from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport useColorDarkenLighten from '~/utils/react/hooks/useColorDarkenLighten';\nimport { useCustomHeaderChangePosition } from '~/utils/react/hooks/useHeader';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport { useHeaderStyle } from '~/store/layout/useHeaderStyle';\nimport { useLayout } from '~/store/layout/useLayout';\nimport { movieTvDetailsPages } from '~/constants/tabLinks';\nimport { MediaBackgroundImage, MediaDetail } from '~/components/media/MediaDetail';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport ErrorBoundaryView from '~/components/elements/shared/ErrorBoundaryView';\nimport TabLink from '~/components/elements/tab/TabLink';\nimport { backgroundStyles } from '~/components/styles/primitives';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const { tvId } = params;\n  const tid = Number(tvId);\n  if (!tid) throw new Response('Not Found', { status: 404 });\n\n  const [detail, imdbId] = await Promise.all([getTvShowDetail(tid, locale), getTvShowIMDBId(tid)]);\n  if (!detail) throw new Response('Not Found', { status: 404 });\n  const extractColorImageUrl =\n    process.env.NODE_ENV === 'development'\n      ? TMDB.backdropUrl(detail?.backdrop_path || detail?.poster_path || '', 'w300')\n      : `https://corsproxy.io/?${encodeURIComponent(\n          TMDB.backdropUrl(detail?.backdrop_path || detail?.poster_path || '', 'w300'),\n        )}`;\n  let nameEng =\n    detail?.original_language === 'en'\n      ? detail?.original_name\n      : locale === 'en'\n      ? detail?.name\n      : '';\n  if (detail?.original_language !== 'en' && locale !== 'en') {\n    const [detailEng, imdbRating, fimg] = await Promise.all([\n      getTvShowDetail(tid, 'en-US'),\n      imdbId && process.env.IMDB_API_URL !== undefined ? getImdbRating(imdbId) : undefined,\n      fetch(extractColorImageUrl),\n    ]);\n    nameEng = detailEng?.name || '';\n    const fimgb = Buffer.from(await fimg.arrayBuffer());\n    const palette =\n      (detail?.backdrop_path || detail?.poster_path) && fimgb\n        ? await Vibrant.from(fimgb).getPalette()\n        : undefined;\n    return json(\n      {\n        detail: {\n          ...detail,\n          color: palette\n            ? Object.values(palette).sort((a, b) =>\n                a?.population === undefined || b?.population === undefined\n                  ? 0\n                  : b.population - a.population,\n              )[0]?.hex\n            : undefined,\n          nameEng,\n        },\n        imdbRating,\n      },\n      {\n        headers: { 'Cache-Control': CACHE_CONTROL.detail },\n      },\n    );\n  }\n\n  const [imdbRating, fimg] = await Promise.all([\n    imdbId && process.env.IMDB_API_URL !== undefined ? getImdbRating(imdbId) : undefined,\n    fetch(extractColorImageUrl),\n  ]);\n  const fimgb = Buffer.from(await fimg.arrayBuffer());\n  const palette =\n    detail?.backdrop_path || detail?.poster_path\n      ? await Vibrant.from(fimgb).getPalette()\n      : undefined;\n  return json(\n    {\n      detail: {\n        ...detail,\n        color: palette\n          ? Object.values(palette).sort((a, b) =>\n              a?.population === undefined || b?.population === undefined\n                ? 0\n                : b.population - a.population,\n            )[0]?.hex\n          : undefined,\n        nameEng,\n      },\n      imdbRating,\n      translations: undefined,\n    },\n    {\n      headers: { 'Cache-Control': CACHE_CONTROL.detail },\n    },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader>(({ data, params }) => {\n  if (!data) {\n    return [];\n  }\n  const { detail } = data;\n  const { name, overview } = detail || {};\n  const tvTitle = name || '';\n  return [\n    { name: 'description', content: overview },\n    { property: 'og:description', content: overview },\n    { name: 'twitter:description', content: overview },\n    {\n      property: 'og:image',\n      content: `https://sorachill.vercel.app/api/ogimage?m=${params.tvId}&mt=tv`,\n    },\n    {\n      name: 'keywords',\n      content: `Watch ${tvTitle}, Stream ${tvTitle}, Watch ${tvTitle} HD, Online ${tvTitle}, Streaming ${tvTitle}, English, Subtitle ${tvTitle}, English Subtitle`,\n    },\n    {\n      name: 'twitter:image',\n      content: `https://sorachill.vercel.app/api/ogimage?m=${params.tvId}&mt=tv`,\n    },\n  ];\n});\n\nexport const handle: Handle = {\n  breadcrumb: ({ match }) => (\n    <BreadcrumbItem to={`/tv-shows/${match.params.tvId}`} key={`tv-shows-${match.params.tvId}`}>\n      {(match.data as { detail: ITvShowDetail })?.detail?.name ||\n        (match.data as { detail: ITvShowDetail })?.detail?.original_name ||\n        match.params.tvId}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ match, t }) => ({\n    title: (match.data as { detail: ITvShowDetail })?.detail?.name || '',\n    subtitle: t('overview'),\n    showImage: (match.data as { detail: ITvShowDetail })?.detail?.poster_path !== undefined,\n    imageUrl: TMDB?.posterUrl(\n      (match.data as { detail: ITvShowDetail })?.detail?.poster_path || '',\n      'w92',\n    ),\n  }),\n  preventScrollToTop: true,\n  disableLayoutPadding: true,\n  customHeaderBackgroundColor: true,\n  customHeaderChangeColorOnScroll: true,\n};\n\nconst TvShowDetail = () => {\n  const { detail, imdbRating } = useLoaderData<typeof loader>();\n  const { state } = useLocation();\n  const isHydrated = useHydrated();\n  const { backgroundColor } = useColorDarkenLighten(detail?.color);\n  const { sidebarBoxedMode } = useSoraSettings();\n  const { viewportRef, scrollY } = useLayout((scrollState) => scrollState);\n  const { setBackgroundColor, startChangeScrollPosition } = useHeaderStyle(\n    (headerStyle) => headerStyle,\n  );\n  const tabLinkRef = useRef<HTMLDivElement>(null);\n  const tablinkIntersection = useIntersectionObserver(tabLinkRef, {\n    root: viewportRef,\n    rootMargin: sidebarBoxedMode ? '-180px 0px 0px 0px' : '-165px 0px 0px 0px',\n    threshold: [0.5],\n  });\n  const paddingTop = useTransform(\n    scrollY,\n    [0, startChangeScrollPosition, startChangeScrollPosition + 100],\n    [16, 16, startChangeScrollPosition ? 0 : 16],\n  );\n  const paddingBottom = useTransform(\n    scrollY,\n    [0, startChangeScrollPosition, startChangeScrollPosition + 100],\n    [32, 32, startChangeScrollPosition ? 0 : 32],\n  );\n\n  useCustomHeaderChangePosition(tablinkIntersection);\n\n  useEffect(() => {\n    if (startChangeScrollPosition) {\n      setBackgroundColor(backgroundColor);\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [backgroundColor, startChangeScrollPosition]);\n\n  const currentTime = state && (state as { currentTime: number | undefined }).currentTime;\n  const backdropPath = detail?.backdrop_path\n    ? TMDB?.backdropUrl(detail?.backdrop_path || '', 'w1280')\n    : undefined;\n\n  return (\n    <>\n      <MediaBackgroundImage backdropPath={backdropPath} backgroundColor={backgroundColor} />\n      <div className=\"relative top-[-80px] w-full sm:top-[-200px]\">\n        <MediaDetail\n          type=\"tv\"\n          item={detail}\n          imdbRating={imdbRating}\n          color={detail.color}\n          trailerTime={currentTime}\n        />\n        <div className=\"flex w-full flex-col items-center justify-center\">\n          <motion.div\n            className=\"sticky top-[61px] z-[1000] flex w-full justify-center transition-[padding] duration-100 ease-in-out\"\n            style={{\n              backgroundColor: isHydrated ? backgroundColor : 'transparent',\n              paddingTop,\n              paddingBottom,\n            }}\n            ref={tabLinkRef}\n          >\n            <div\n              className={backgroundStyles({ tablink: true })}\n              style={{ backgroundColor: isHydrated ? backgroundColor : 'transparent' }}\n            />\n            <TabLink pages={movieTvDetailsPages} linkTo={`/tv-shows/${detail?.id}`} />\n          </motion.div>\n          <Outlet />\n        </div>\n      </div>\n    </>\n  );\n};\n\nexport function ErrorBoundary() {\n  return (\n    <ErrorBoundaryView\n      statusHandlers={{\n        404: ({ params }) => <p>There is no series with the ID: {params.tvId}</p>,\n      }}\n    />\n  );\n}\n\nexport default TvShowDetail;\n"
  },
  {
    "path": "app/routes/tv-shows+/$tvId_.season.$seasonId+/_index.tsx",
    "content": "import { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as tvSeasonIdLoader } from '~/routes/tv-shows+/$tvId_.season.$seasonId';\nimport type { IEpisode } from '~/services/tmdb/tmdb.types';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport ListEpisodes from '~/components/elements/shared/ListEpisodes';\n\nexport const meta = mergeMeta<\n  null,\n  { 'routes/tv-shows+/$tvId_.season.$seasonId': typeof tvSeasonIdLoader }\n>(({ params, matches }) => {\n  const tvSeasonData = matches.find(\n    (match) => match.id === 'routes/tv-shows+/$tvId_.season.$seasonId',\n  )?.data;\n  if (!tvSeasonData?.seasonDetail) {\n    return [\n      { title: 'Missing Season' },\n      { name: 'description', content: `There is no season with the ID: ${params.seasonId}` },\n    ];\n  }\n  const { detail, seasonDetail } = tvSeasonData;\n  const { name } = detail || {};\n  return [\n    { title: `Sora - ${name} ${seasonDetail?.name || ''}` },\n    {\n      property: 'og:url',\n      content: `https://sorachill.vercel.app/tv-shows/${params.tvId}/season/${params.seasonId}/`,\n    },\n    { property: 'og:title', content: `Sora - ${name} ${seasonDetail?.name || ''}` },\n    { name: 'twitter:title', content: `Sora - ${name} ${seasonDetail?.name || ''}` },\n  ];\n});\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/tv-shows/${match.params.tvId}/season/${match.params.seasonId}/`}\n      key={`tv-shows-${match.params.tvId}-season-${match.params.seasonId}-episodes`}\n    >\n      {t('episodes')}\n    </BreadcrumbItem>\n  ),\n};\n\nconst Episodes = () => {\n  const seasonData = useTypedRouteLoaderData('routes/tv-shows+/$tvId_.season.$seasonId');\n  const seasonDetail = seasonData && seasonData.seasonDetail;\n  const detail = seasonData && seasonData.detail;\n  return (\n    <div className=\"flex w-full flex-col items-center justify-center px-3 sm:w-2/3 sm:px-5\">\n      <ListEpisodes\n        type=\"tv\"\n        id={detail?.id}\n        episodes={seasonDetail?.episodes as unknown as IEpisode[]}\n        season={seasonDetail?.season_number}\n        providers={seasonData?.providers || []}\n      />\n    </div>\n  );\n};\n\nexport default Episodes;\n"
  },
  {
    "path": "app/routes/tv-shows+/$tvId_.season.$seasonId+/cast.tsx",
    "content": "import { useRef } from 'react';\nimport { Pagination } from '@nextui-org/pagination';\nimport { useMediaQuery } from '@react-hookz/web';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as tvSeasonIdLoader } from '~/routes/tv-shows+/$tvId_.season.$seasonId';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getTvSeasonCredits } from '~/services/tmdb/tmdb.server';\nimport type { ISeasonDetail, ITvShowDetail } from '~/services/tmdb/tmdb.types';\nimport { postFetchDataHandler } from '~/services/tmdb/utils.server';\nimport TMDB from '~/utils/media';\nimport useSplitArrayIntoPage from '~/utils/react/hooks/useSplitArrayIntoPage';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  await authenticate(request, undefined, true);\n\n  const locale = await i18next.getLocale(request);\n  const { tvId, seasonId } = params;\n  const tid = Number(tvId);\n\n  if (!tid) throw new Response('Not found', { status: 404 });\n  const credits = await getTvSeasonCredits(tid, Number(seasonId), locale);\n\n  if (!credits) throw new Response('Not found', { status: 404 });\n\n  return json(\n    { cast: [...postFetchDataHandler(credits.cast, 'people')] },\n    { headers: { 'Cache-Control': CACHE_CONTROL.detail } },\n  );\n};\n\nexport const meta = mergeMeta<\n  null,\n  { 'routes/tv-shows+/$tvId_.season.$seasonId': typeof tvSeasonIdLoader }\n>(({ params, matches }) => {\n  const tvSeasonData = matches.find(\n    (match) => match.id === 'routes/tv-shows+/$tvId_.season.$seasonId',\n  )?.data;\n  if (!tvSeasonData?.seasonDetail) {\n    return [\n      { title: 'Missing Season' },\n      { name: 'description', content: `There is no season with the ID: ${params.seasonId}` },\n    ];\n  }\n  const { detail, seasonDetail } = tvSeasonData;\n  const { name } = detail || {};\n  return [\n    { title: `Sora - ${name} ${seasonDetail?.name || ''} - Cast` },\n    {\n      property: 'og:url',\n      content: `https://sorachill.vercel.app/tv-shows/${params.tvId}/season/${params.seasonId}/cast`,\n    },\n    { property: 'og:title', content: `Sora - ${name} ${seasonDetail?.name || ''} - Cast` },\n    { name: 'twitter:title', content: `Sora - ${name} ${seasonDetail?.name || ''} - Cast` },\n  ];\n});\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/tv-shows/${match.params.tvId}/season/${match.params.seasonId}/cast`}\n      key={`tv-shows-${match.params.tvId}-season-${match.params.seasonId}-cast`}\n    >\n      {t('cast')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch\n      ? `${\n          (parentMatch?.data as { detail: ITvShowDetail })?.detail?.name ||\n          (parentMatch?.data as { detail: ITvShowDetail })?.detail?.original_name\n        } - ${(parentMatch?.data as { seasonDetail: ISeasonDetail })?.seasonDetail?.name}`\n      : '',\n    subtitle: t('cast'),\n    showImage: parentMatch\n      ? (parentMatch?.data as { seasonDetail: ISeasonDetail })?.seasonDetail?.poster_path !==\n        undefined\n      : false,\n    imageUrl: TMDB.posterUrl(\n      parentMatch\n        ? (parentMatch?.data as { seasonDetail: ISeasonDetail })?.seasonDetail?.poster_path || ''\n        : '',\n      'w92',\n    ),\n  }),\n};\n\nconst TvSeasonCastPage = () => {\n  const { cast } = useLoaderData<typeof loader>();\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const ref = useRef<HTMLDivElement>(null);\n  const { gotoPage, currentPage, maxPage, currentData } = useSplitArrayIntoPage(cast || [], 20);\n\n  return (\n    <div className=\"mt-3 flex w-full max-w-[1920px] flex-col gap-y-4 px-3 sm:px-3.5 xl:px-4 2xl:px-5\">\n      <div ref={ref} />\n      <MediaList listType=\"grid\" items={currentData} itemsType=\"people\" />\n      {maxPage > 1 ? (\n        <div className=\"mt-7 flex flex-row justify-center\">\n          <Pagination\n            // showControls={!isSm}\n            total={maxPage}\n            initialPage={currentPage}\n            // shadow\n            onChange={(page) => {\n              gotoPage(page);\n              // scroll to top after changing page\n              ref.current?.scrollIntoView({\n                behavior: 'instant',\n                block: 'center',\n                inline: 'nearest',\n              });\n            }}\n            {...(isSm && { size: 'sm' })}\n          />\n        </div>\n      ) : null}\n    </div>\n  );\n};\n\nexport default TvSeasonCastPage;\n"
  },
  {
    "path": "app/routes/tv-shows+/$tvId_.season.$seasonId+/crew.tsx",
    "content": "import { useRef } from 'react';\nimport { Pagination } from '@nextui-org/pagination';\nimport { useMediaQuery } from '@react-hookz/web';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as tvSeasonIdLoader } from '~/routes/tv-shows+/$tvId_.season.$seasonId';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getTvSeasonCredits } from '~/services/tmdb/tmdb.server';\nimport type { ISeasonDetail, ITvShowDetail } from '~/services/tmdb/tmdb.types';\nimport { postFetchDataHandler } from '~/services/tmdb/utils.server';\nimport TMDB from '~/utils/media';\nimport useSplitArrayIntoPage from '~/utils/react/hooks/useSplitArrayIntoPage';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const { tvId, seasonId } = params;\n  const tid = Number(tvId);\n\n  if (!tid) throw new Response('Not found', { status: 404 });\n  const credits = await getTvSeasonCredits(tid, Number(seasonId), locale);\n\n  if (!credits) throw new Response('Not found', { status: 404 });\n\n  return json(\n    { crew: [...postFetchDataHandler(credits.crew, 'people')] },\n    { headers: { 'Cache-Control': CACHE_CONTROL.detail } },\n  );\n};\n\nexport const meta = mergeMeta<\n  null,\n  { 'routes/tv-shows+/$tvId_.season.$seasonId': typeof tvSeasonIdLoader }\n>(({ params, matches }) => {\n  const tvSeasonData = matches.find(\n    (match) => match.id === 'routes/tv-shows+/$tvId_.season.$seasonId',\n  )?.data;\n  if (!tvSeasonData?.seasonDetail) {\n    return [\n      { title: 'Missing Season' },\n      { name: 'description', content: `There is no season with the ID: ${params.seasonId}` },\n    ];\n  }\n  const { detail, seasonDetail } = tvSeasonData;\n  const { name } = detail || {};\n  return [\n    { title: `Sora - ${name} ${seasonDetail?.name || ''} - Crew` },\n    {\n      property: 'og:url',\n      content: `https://sorachill.vercel.app/tv-shows/${params.tvId}/season/${params.seasonId}/crew`,\n    },\n    { property: 'og:title', content: `Sora - ${name} ${seasonDetail?.name || ''} - Crew` },\n    { name: 'twitter:title', content: `Sora - ${name} ${seasonDetail?.name || ''} - Crew` },\n  ];\n});\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/tv-shows/${match.params.tvId}/season/${match.params.seasonId}/crew`}\n      key={`tv-shows-${match.params.tvId}-season-${match.params.seasonId}-crew`}\n    >\n      {t('crew')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch\n      ? `${\n          (parentMatch?.data as { detail: ITvShowDetail })?.detail?.name ||\n          (parentMatch?.data as { detail: ITvShowDetail })?.detail?.original_name\n        } - ${(parentMatch?.data as { seasonDetail: ISeasonDetail })?.seasonDetail?.name}`\n      : '',\n    subtitle: t('crew'),\n    showImage: parentMatch\n      ? (parentMatch?.data as { seasonDetail: ISeasonDetail })?.seasonDetail?.poster_path !==\n        undefined\n      : false,\n    imageUrl: TMDB.posterUrl(\n      parentMatch\n        ? (parentMatch?.data as { seasonDetail: ISeasonDetail })?.seasonDetail?.poster_path || ''\n        : '',\n      'w92',\n    ),\n  }),\n};\n\nconst TvSeasonCrewPage = () => {\n  const { crew } = useLoaderData<typeof loader>();\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const ref = useRef<HTMLDivElement>(null);\n  const { gotoPage, currentPage, maxPage, currentData } = useSplitArrayIntoPage(crew || [], 20);\n\n  return (\n    <div className=\"mt-3 flex w-full max-w-[1920px] flex-col gap-y-4 px-3 sm:px-3.5 xl:px-4 2xl:px-5\">\n      <div ref={ref} />\n      <MediaList items={currentData} itemsType=\"people\" listType=\"grid\" />\n      {maxPage > 1 ? (\n        <div className=\"mt-7 flex flex-row justify-center\">\n          <Pagination\n            // showControls={!isSm}\n            total={maxPage}\n            initialPage={currentPage}\n            // shadow\n            onChange={(page) => {\n              gotoPage(page);\n              ref.current?.scrollIntoView({\n                behavior: 'smooth',\n                block: 'start',\n                inline: 'center',\n              });\n            }}\n            {...(isSm && { size: 'sm' })}\n          />\n        </div>\n      ) : null}\n    </div>\n  );\n};\n\nexport default TvSeasonCrewPage;\n"
  },
  {
    "path": "app/routes/tv-shows+/$tvId_.season.$seasonId+/photos.tsx",
    "content": "import * as React from 'react';\nimport { Spacer } from '@nextui-org/spacer';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { useTranslation } from 'react-i18next';\nimport { Gallery, Item, type GalleryProps } from 'react-photoswipe-gallery';\nimport { MimeType } from 'remix-image';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as tvSeasonIdLoader } from '~/routes/tv-shows+/$tvId_.season.$seasonId';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getTvSeasonImages } from '~/services/tmdb/tmdb.server';\nimport type { ISeasonDetail, ITvShowDetail } from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport Image from '~/components/elements/Image';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const { tvId, seasonId } = params;\n  const tid = Number(tvId);\n  if (!tid) throw new Response('Not Found', { status: 404 });\n\n  const images = await getTvSeasonImages(tid, Number(seasonId), locale);\n  if (!images) throw new Response('Not Found', { status: 404 });\n\n  return json({ images }, { headers: { 'Cache-Control': CACHE_CONTROL.detail } });\n};\n\nexport const meta = mergeMeta<\n  null,\n  { 'routes/tv-shows+/$tvId_.season.$seasonId': typeof tvSeasonIdLoader }\n>(({ params, matches }) => {\n  const tvSeasonData = matches.find(\n    (match) => match.id === 'routes/tv-shows+/$tvId_.season.$seasonId',\n  )?.data;\n  if (!tvSeasonData?.seasonDetail) {\n    return [\n      { title: 'Missing Season' },\n      { name: 'description', content: `There is no season with the ID: ${params.seasonId}` },\n    ];\n  }\n  const { detail, seasonDetail } = tvSeasonData;\n  const { name } = detail || {};\n  return [\n    { title: `Sora - ${name} ${seasonDetail?.name || ''} - Photos` },\n    {\n      property: 'og:url',\n      content: `https://sorachill.vercel.app/tv-shows/${params.tvId}/season/${params.seasonId}/photos`,\n    },\n    { property: 'og:title', content: `Sora - ${name} ${seasonDetail?.name || ''} - Photos` },\n    { name: 'twitter:title', content: `Sora - ${name} ${seasonDetail?.name || ''} - Photos` },\n  ];\n});\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/tv-shows/${match.params.tvId}/season/${match.params.seasonId}/photos`}\n      key={`tv-shows-${match.params.tvId}-season-${match.params.seasonId}-photos`}\n    >\n      {t('photos')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch\n      ? `${\n          (parentMatch?.data as { detail: ITvShowDetail })?.detail?.name ||\n          (parentMatch?.data as { detail: ITvShowDetail })?.detail?.original_name\n        } - ${(parentMatch?.data as { seasonDetail: ISeasonDetail })?.seasonDetail?.name}`\n      : '',\n    subtitle: t('photos'),\n    showImage: parentMatch\n      ? (parentMatch?.data as { seasonDetail: ISeasonDetail })?.seasonDetail?.poster_path !==\n        undefined\n      : false,\n    imageUrl: TMDB.posterUrl(\n      parentMatch\n        ? (parentMatch?.data as { seasonDetail: ISeasonDetail })?.seasonDetail?.poster_path || ''\n        : '',\n      'w92',\n    ),\n  }),\n};\n\nconst PhotosPage = () => {\n  const { images } = useLoaderData<typeof loader>();\n  const { t } = useTranslation();\n  const tvData = useTypedRouteLoaderData('routes/tv-shows+/$tvId_.season.$seasonId');\n\n  const uiElements: GalleryProps['uiElements'] = [\n    {\n      name: 'custom-rotate-button',\n      ariaLabel: 'Rotate',\n      order: 9,\n      isButton: true,\n      html: {\n        isCustomSVG: true,\n        inner:\n          '<path d=\"M13.887 6.078C14.258 6.234 14.5 6.598 14.5 7V8.517C18.332 8.657 21.258 10.055 23.15 12.367 24.519 14.041 25.289 16.13 25.496 18.409A1 1 0 0123.504 18.591C23.327 16.645 22.68 14.952 21.601 13.633 20.156 11.867 17.831 10.653 14.5 10.517V12A1.002 1.002 0 0112.779 12.693L10.304 10.121A1.002 1.002 0 0110.324 8.713L12.8 6.286A1 1 0 0113.887 6.078ZM7.5 16A1.5 1.5 0 006 17.5V24.5A1.5 1.5 0 007.5 26H17.5A1.5 1.5 0 0019 24.5V17.5A1.5 1.5 0 0017.5 16H7.5Z\" id=\"pswp__icn-rotate\"/>',\n        outlineID: 'pswp__icn-rotate',\n      },\n      appendTo: 'bar',\n      onClick: (_, __, pswpInstance) => {\n        const item = pswpInstance.currSlide?.content.element;\n\n        const prevRotateAngle = Number(item?.dataset.rotateAngel) || 0;\n        const rotateAngle = prevRotateAngle === 270 ? 0 : prevRotateAngle + 90;\n\n        // add slide rotation\n        if (item) {\n          item.style.transform = `${item.style.transform.replace(\n            `rotate(-${prevRotateAngle}deg)`,\n            '',\n          )} rotate(-${rotateAngle}deg)`;\n          item.dataset.rotateAngel = String(rotateAngle);\n        }\n      },\n      onInit: (_, pswpInstance) => {\n        // remove applied rotation on slide change\n        // https://photoswipe.com/events/#slide-content-events\n        pswpInstance.on('contentRemove', () => {\n          const item = pswpInstance.currSlide?.content.element;\n          if (item) {\n            item.style.transform = `${item.style.transform.replace(\n              `rotate(-${item.dataset.rotateAngel || 0}deg)`,\n              '',\n            )}`;\n            delete item.dataset.rotateAngel;\n          }\n        });\n      },\n    },\n  ];\n  return (\n    <div className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\">\n      <Spacer y={5} />\n      <h5 className=\"flex w-full justify-center\">\n        <strong>{t('posters')}</strong>\n      </h5>\n      <Spacer y={2.5} />\n      <Gallery withCaption withDownloadButton uiElements={uiElements}>\n        <div className=\"grid grid-cols-1 justify-center gap-3 xs:grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6\">\n          {images?.posters?.map((image) => (\n            <Item\n              key={image.file_path}\n              cropped\n              original={TMDB.profileUrl(image?.file_path, 'original')}\n              thumbnail={TMDB.profileUrl(image?.file_path, 'w185')}\n              alt={`Photo of ${tvData?.detail?.name} image size ${image.width}x${image.height}`}\n              caption={`Photo of ${tvData?.detail?.name} size ${image.width}x${image.height}`}\n              width={image.width}\n              height={image.height}\n            >\n              {({ ref, open }) => (\n                <Image\n                  src={TMDB.profileUrl(image?.file_path, 'w185')}\n                  ref={ref as React.MutableRefObject<HTMLImageElement>}\n                  onClick={open}\n                  alt={`Photo of ${tvData?.detail?.name} image size ${image.width}x${image.height}`}\n                  radius=\"lg\"\n                  classNames={{\n                    img: 'h-auto min-w-[120px] cursor-pointer object-cover 2xs:min-w-[185px]',\n                  }}\n                  title={tvData?.detail?.name}\n                  placeholder=\"empty\"\n                  options={{\n                    contentType: MimeType.WEBP,\n                  }}\n                />\n              )}\n            </Item>\n          ))}\n        </div>\n      </Gallery>\n    </div>\n  );\n};\n\nexport default PhotosPage;\n"
  },
  {
    "path": "app/routes/tv-shows+/$tvId_.season.$seasonId+/videos.tsx",
    "content": "import { useEffect, useRef, useState } from 'react';\nimport { Card, CardBody, CardFooter } from '@nextui-org/card';\nimport { useMediaQuery } from '@react-hookz/web';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useFetcher, useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { AnimatePresence, motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\nimport { MimeType } from 'remix-image';\n\nimport type { Handle } from '~/types/handle';\nimport type { loader as tvSeasonIdLoader } from '~/routes/tv-shows+/$tvId_.season.$seasonId';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getTvSeasonVideos } from '~/services/tmdb/tmdb.server';\nimport type { ISeasonDetail, ITvShowDetail } from '~/services/tmdb/tmdb.types';\nimport type { IYoutubeItem } from '~/services/youtube/youtube.types';\nimport TMDB from '~/utils/media';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport { Dialog, DialogContent, DialogTrigger } from '~/components/elements/Dialog';\nimport WatchTrailer, { type Trailer } from '~/components/elements/dialog/WatchTrailerDialog';\nimport Image from '~/components/elements/Image';\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '~/components/elements/tab/Tabs';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const { tvId, seasonId } = params;\n  const tid = Number(tvId);\n\n  if (!tid) throw new Response('Not Found', { status: 404 });\n  const videos = await getTvSeasonVideos(tid, Number(seasonId), locale);\n\n  if (!videos) throw new Response('Not Found', { status: 404 });\n\n  return json({ videos }, { headers: { 'Cache-Control': CACHE_CONTROL.detail } });\n};\n\nexport const meta = mergeMeta<\n  null,\n  { 'routes/tv-shows+/$tvId_.season.$seasonId': typeof tvSeasonIdLoader }\n>(({ params, matches }) => {\n  const tvSeasonData = matches.find(\n    (match) => match.id === 'routes/tv-shows+/$tvId_.season.$seasonId',\n  )?.data;\n  if (!tvSeasonData?.seasonDetail) {\n    return [\n      { title: 'Missing Season' },\n      { name: 'description', content: `There is no season with the ID: ${params.seasonId}` },\n    ];\n  }\n  const { detail, seasonDetail } = tvSeasonData;\n  const { name } = detail || {};\n  return [\n    { title: `Sora - ${name} ${seasonDetail?.name || ''} - Videos` },\n    {\n      property: 'og:url',\n      content: `https://sorachill.vercel.app/tv-shows/${params.tvId}/season/${params.seasonId}/videos`,\n    },\n    { property: 'og:title', content: `Sora - ${name} ${seasonDetail?.name || ''} - Videos` },\n    { name: 'twitter:title', content: `Sora - ${name} ${seasonDetail?.name || ''} - Videos` },\n  ];\n});\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <BreadcrumbItem\n      to={`/tv-shows/${match.params.tvId}/season/${match.params.seasonId}/videos`}\n      key={`tv-shows-${match.params.tvId}-season-${match.params.seasonId}-videos`}\n    >\n      {t('videos')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ parentMatch, t }) => ({\n    title: parentMatch\n      ? `${\n          (parentMatch?.data as { detail: ITvShowDetail })?.detail?.name ||\n          (parentMatch?.data as { detail: ITvShowDetail })?.detail?.original_name\n        } - ${(parentMatch?.data as { seasonDetail: ISeasonDetail })?.seasonDetail?.name}`\n      : '',\n    subtitle: t('videos'),\n    showImage: parentMatch\n      ? (parentMatch?.data as { seasonDetail: ISeasonDetail })?.seasonDetail?.poster_path !==\n        undefined\n      : false,\n    imageUrl: TMDB.posterUrl(\n      parentMatch\n        ? (parentMatch?.data as { seasonDetail: ISeasonDetail })?.seasonDetail?.poster_path || ''\n        : '',\n      'w92',\n    ),\n  }),\n};\n\nconst VideosPage = () => {\n  const { videos } = useLoaderData<typeof loader>();\n  const fetcher = useFetcher<{ youtubeVideo: IYoutubeItem[] }>();\n  const { t } = useTranslation();\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const [activeType, setActiveType] = useState('trailer');\n  const [activeTypeVideos, setActiveTypeVideos] = useState<IYoutubeItem[] | []>([]);\n  const [visible, setVisible] = useState(false);\n  const [trailer, setTrailer] = useState<Trailer>({});\n  const underlineRef = useRef<HTMLDivElement>(null);\n\n  const typesVideo = [\n    {\n      id: 'trailer',\n      activeVideo: 'trailer',\n    },\n    {\n      id: 'teaser',\n      activeVideo: 'teaser',\n    },\n    {\n      id: 'clip',\n      activeVideo: 'clip',\n    },\n    {\n      id: 'behind_the_scenes',\n      activeVideo: 'behind-the-scenes',\n    },\n    {\n      id: 'bloopers',\n      activeVideo: 'bloopers',\n    },\n    {\n      id: 'featurette',\n      activeVideo: 'featurette',\n    },\n    {\n      id: 'opening_credits',\n      activeVideo: 'opening-credits',\n    },\n  ];\n  useEffect(() => {\n    if (videos) {\n      let activeVideo = [];\n      const activeTypeVideo = typesVideo.find((item) => item.id === activeType);\n      activeVideo = videos.results?.filter((video) => video.type === activeTypeVideo?.activeVideo);\n      const keyVideo = activeVideo.map((item) => item.key).join(',');\n      if (keyVideo) fetcher.load(`/api/youtube-video?id=${keyVideo}`);\n      else setActiveTypeVideos([]);\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [activeType, videos]);\n  useEffect(() => {\n    if (fetcher.data && fetcher.data.youtubeVideo) {\n      // @ts-expect-error\n      setActiveTypeVideos(fetcher.data.youtubeVideo);\n    }\n  }, [fetcher.data]);\n\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  const handleDragEnd = (event: MouseEvent | PointerEvent | TouchEvent, info: PanInfo) => {\n    const currentTab = typesVideo.find((type) => type.id === activeType);\n    if (info.offset?.x > 100) {\n      // swipe right\n      if (currentTab?.id === 'trailer') {\n        setActiveType('opening_credits');\n      } else {\n        const index = typesVideo.findIndex((type) => type.id === activeType);\n        setActiveType(typesVideo[index - 1].id);\n      }\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      // swipe left\n      if (currentTab?.id === 'opening_credits') {\n        setActiveType('trailer');\n      } else {\n        const index = typesVideo.findIndex((type) => type.id === activeType);\n        setActiveType(typesVideo[index + 1].id);\n      }\n    }\n  };\n\n  return (\n    <div className=\"mt-3 flex w-full max-w-[1920px] flex-col gap-x-0 gap-y-4 px-3 sm:flex-row sm:items-stretch sm:justify-center sm:gap-x-4 sm:gap-y-0 sm:px-3.5 xl:px-4 2xl:px-5\">\n      <Tabs\n        defaultValue={activeType}\n        value={activeType}\n        orientation={isSm ? 'horizontal' : 'vertical'}\n        onValueChange={(value) => setActiveType(value)}\n        className=\"w-full\"\n      >\n        <TabsList>\n          {typesVideo.map((type) => (\n            <TabsTrigger key={type.id} value={type.id} className=\"relative\">\n              <h6 className=\"!m-0\">{t(type.activeVideo)}</h6>\n              {activeType === type.id ? (\n                <motion.div\n                  className=\"absolute overflow-hidden rounded-small bg-default-foreground data-[orientation=horizontal]:bottom-0 data-[orientation=vertical]:left-0 data-[orientation=horizontal]:h-1 data-[orientation=vertical]:h-1/2 data-[orientation=horizontal]:w-1/2 data-[orientation=vertical]:w-1\"\n                  layoutId=\"video-underline\"\n                  data-orientation={isSm ? 'horizontal' : 'vertical'}\n                  ref={underlineRef}\n                />\n              ) : null}\n            </TabsTrigger>\n          ))}\n        </TabsList>\n        <Dialog open={visible} onOpenChange={setVisible}>\n          <AnimatePresence mode=\"wait\">\n            <TabsContent value={activeType}>\n              <motion.div\n                initial={{ opacity: 0 }}\n                animate={{ opacity: 1 }}\n                exit={{ opacity: 0 }}\n                transition={{ ease: 'easeInOut', duration: 0.3 }}\n                drag={isMobile ? 'x' : false}\n                dragConstraints={{ left: 0, right: 0 }}\n                dragElastic={0.4}\n                onDragEnd={handleDragEnd}\n                dragDirectionLock\n              >\n                <div className=\"grid w-full grid-cols-1 justify-items-center gap-4 lg:grid-cols-2 3xl:grid-cols-3 4xl:grid-cols-4\">\n                  {activeTypeVideos\n                    ? activeTypeVideos.map((video) => (\n                        <DialogTrigger asChild key={video?.id}>\n                          <Card\n                            isPressable\n                            isHoverable\n                            role=\"figure\"\n                            className=\"w-[320px] data-[hover=true]:ring-2 data-[hover=true]:ring-focus\"\n                            onPress={() => {\n                              const videoPlay = videos?.results?.find(\n                                (item) => item.key === video.id,\n                              );\n                              if (videoPlay) {\n                                setTrailer(videoPlay);\n                              }\n                            }}\n                          >\n                            <CardBody className=\"shrink-0 grow-0 overflow-hidden p-0\">\n                              <Image\n                                src={video?.snippet?.thumbnails?.medium?.url}\n                                width={320}\n                                height={180}\n                                alt={video?.snippet?.title}\n                                loading=\"lazy\"\n                                title={video?.snippet?.title}\n                                placeholder=\"empty\"\n                                options={{ contentType: MimeType.WEBP }}\n                                responsive={[{ size: { width: 320, height: 180 } }]}\n                              />\n                            </CardBody>\n                            <CardFooter className=\"flex flex-col items-start justify-start\">\n                              <h6 className=\"!m-0 text-left font-semibold\">\n                                {video?.snippet?.title}\n                              </h6>\n                              <p className=\"opacity-70\">{video?.snippet?.channelTitle}</p>\n                            </CardFooter>\n                          </Card>\n                        </DialogTrigger>\n                      ))\n                    : null}\n                </div>\n              </motion.div>\n            </TabsContent>\n          </AnimatePresence>\n          <DialogContent className=\"overflow-hidden !p-0\">\n            <WatchTrailer trailer={trailer} />\n          </DialogContent>\n        </Dialog>\n      </Tabs>\n    </div>\n  );\n};\n\nexport default VideosPage;\n"
  },
  {
    "path": "app/routes/tv-shows+/$tvId_.season.$seasonId.tsx",
    "content": "import { useEffect, useRef } from 'react';\nimport { Card, CardBody, CardHeader } from '@nextui-org/card';\nimport { useIntersectionObserver, useMeasure, useMediaQuery } from '@react-hookz/web';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { Outlet, useLoaderData, useParams } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, useTransform } from 'framer-motion';\nimport Vibrant from 'node-vibrant';\nimport { useTranslation } from 'react-i18next';\nimport { MimeType } from 'remix-image';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport getProviderList from '~/services/provider.server';\nimport { authenticate } from '~/services/supabase';\nimport { getTvSeasonDetail, getTvShowDetail } from '~/services/tmdb/tmdb.server';\nimport type { ISeasonDetail, ITvShowDetail } from '~/services/tmdb/tmdb.types';\nimport TMDB from '~/utils/media';\nimport useColorDarkenLighten from '~/utils/react/hooks/useColorDarkenLighten';\nimport { useCustomHeaderChangePosition } from '~/utils/react/hooks/useHeader';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useSoraSettings } from '~/utils/react/hooks/useLocalStorage';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport { useHeaderStyle } from '~/store/layout/useHeaderStyle';\nimport { useLayout } from '~/store/layout/useLayout';\nimport { tvSeasonDetailPages } from '~/constants/tabLinks';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport Image from '~/components/elements/Image';\nimport ErrorBoundaryView from '~/components/elements/shared/ErrorBoundaryView';\nimport TabLink from '~/components/elements/tab/TabLink';\nimport { backgroundStyles } from '~/components/styles/primitives';\nimport PhotoIcon from '~/assets/icons/PhotoIcon';\nimport BackgroundDefault from '~/assets/images/background-default.jpg';\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const { tvId, seasonId } = params;\n  if (!tvId || !seasonId) throw new Error('Missing params');\n  const tid = Number(tvId);\n  const sid = Number(seasonId);\n\n  // get translators\n\n  const [seasonDetail, detail, detailEng] = await Promise.all([\n    getTvSeasonDetail(tid, sid, locale),\n    getTvShowDetail(tid, locale),\n    getTvShowDetail(tid, 'en-US'),\n  ]);\n  if (!seasonDetail) throw new Response('Not Found', { status: 404 });\n  const title = detailEng?.name || '';\n  const orgTitle = detail?.original_name || '';\n  const year = new Date(seasonDetail?.air_date || '').getFullYear();\n  const season = seasonDetail?.season_number;\n  const extractColorImageUrl =\n    process.env.NODE_ENV === 'development'\n      ? TMDB.backdropUrl(seasonDetail?.poster_path || '', 'w300')\n      : `https://corsproxy.io/?${encodeURIComponent(\n          TMDB.backdropUrl(seasonDetail?.poster_path || '', 'w300'),\n        )}`;\n  const isEnded = detail?.status === 'Ended' || detail?.status === 'Canceled';\n\n  const [providers, fimg] = await Promise.all([\n    getProviderList({\n      type: 'tv',\n      title,\n      orgTitle,\n      year,\n      season,\n      animeId: undefined,\n      animeType: undefined,\n      isEnded,\n      tmdbId: tid,\n    }),\n    fetch(extractColorImageUrl),\n  ]);\n\n  const fimgb = Buffer.from(await fimg.arrayBuffer());\n  const palette =\n    seasonDetail?.poster_path && fimgb ? await Vibrant.from(fimgb).getPalette() : undefined;\n\n  if (providers && providers.length > 0)\n    return json(\n      {\n        detail,\n        seasonDetail,\n        providers,\n        color: palette\n          ? Object.values(palette).sort((a, b) =>\n              a?.population === undefined || b?.population === undefined\n                ? 0\n                : b.population - a.population,\n            )[0]?.hex\n          : undefined,\n      },\n      { headers: { 'Cache-Control': CACHE_CONTROL.season } },\n    );\n\n  return json(\n    {\n      detail,\n      seasonDetail,\n      color: palette\n        ? Object.values(palette).sort((a, b) =>\n            a?.population === undefined || b?.population === undefined\n              ? 0\n              : b.population - a.population,\n          )[0]?.hex\n        : undefined,\n      providers: [],\n    },\n    { headers: { 'Cache-Control': CACHE_CONTROL.season } },\n  );\n};\n\nexport const meta = mergeMeta<typeof loader>(({ data, params }) => {\n  if (!data?.seasonDetail) {\n    return [];\n  }\n  const { seasonDetail } = data;\n  return [\n    { name: 'description', content: seasonDetail?.overview || '' },\n    {\n      property: 'og:url',\n      content: `https://sorachill.vercel.app/tv-shows/${params.tvId}/season/${params.seasonId}`,\n    },\n    { property: 'og:description', content: seasonDetail?.overview || '' },\n    {\n      property: 'og:image',\n      content: `https://sorachill.vercel.app/api/ogimage?m=${params.tvId}&mt=tv`,\n    },\n    { name: 'twitter:description', content: seasonDetail?.overview || '' },\n  ];\n});\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <>\n      <BreadcrumbItem\n        to={`/tv-shows/${match.params.tvId}/`}\n        key={`tv-show-${match.params.tvId}-overview`}\n      >\n        {(match.data as { detail: ITvShowDetail })?.detail?.name ||\n          (match.data as { detail: ITvShowDetail })?.detail?.original_name ||\n          match.params.tvId}\n      </BreadcrumbItem>\n      <BreadcrumbItem\n        to={`/tv-shows/${match.params.tvId}/season/${match.params.seasonId}`}\n        key={`tv-shows-${match.params.tvId}-season-${match.params.seasonId}`}\n      >\n        {t('season')} {match.params.seasonId}\n      </BreadcrumbItem>\n    </>\n  ),\n  miniTitle: ({ match, t }) => ({\n    title: `${\n      (match.data as { detail: ITvShowDetail })?.detail?.name ||\n      (match.data as { detail: ITvShowDetail })?.detail?.original_name\n    } - ${(match.data as { seasonDetail: ISeasonDetail })?.seasonDetail?.name}`,\n    subtitle: t('episodes'),\n    showImage:\n      (match.data as { seasonDetail: ISeasonDetail })?.seasonDetail?.poster_path !== undefined,\n    imageUrl: TMDB.posterUrl(\n      (match.data as { seasonDetail: ISeasonDetail })?.seasonDetail?.poster_path || '',\n      'w92',\n    ),\n  }),\n  disableLayoutPadding: true,\n  customHeaderBackgroundColor: true,\n  customHeaderChangeColorOnScroll: true,\n};\n\nconst TvSeasonDetail = () => {\n  const { detail, seasonDetail, color } = useLoaderData<typeof loader>();\n  const { tvId, seasonId } = useParams();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n  const [size, ref] = useMeasure<HTMLDivElement>();\n  const [imageSize, imageRef] = useMeasure<HTMLDivElement>();\n  const { backgroundColor } = useColorDarkenLighten(color);\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const isXl = useMediaQuery('(max-width: 1280px)', { initializeWithValue: false });\n  const { sidebarBoxedMode } = useSoraSettings();\n  const { viewportRef, scrollY } = useLayout((scrollState) => scrollState);\n  const { setBackgroundColor, startChangeScrollPosition } = useHeaderStyle(\n    (headerState) => headerState,\n  );\n  const tabLinkRef = useRef<HTMLDivElement>(null);\n  const tablinkIntersection = useIntersectionObserver(tabLinkRef, {\n    root: viewportRef,\n    rootMargin: sidebarBoxedMode ? '-180px 0px 0px 0px' : '-165px 0px 0px 0px',\n    threshold: [0.5],\n  });\n  const paddingTop = useTransform(\n    scrollY,\n    [0, startChangeScrollPosition, startChangeScrollPosition + 100],\n    [16, 16, startChangeScrollPosition ? 0 : 16],\n  );\n  const paddingBottom = useTransform(\n    scrollY,\n    [0, startChangeScrollPosition, startChangeScrollPosition + 100],\n    [32, 32, startChangeScrollPosition ? 0 : 32],\n  );\n  useCustomHeaderChangePosition(tablinkIntersection);\n  useEffect(() => {\n    if (startChangeScrollPosition) {\n      setBackgroundColor(backgroundColor);\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [backgroundColor, startChangeScrollPosition]);\n\n  return (\n    <>\n      <Card\n        radius=\"none\"\n        className=\"flex w-full flex-col border-0\"\n        style={{ height: `calc(${size?.height}px + 72px)` }}\n      >\n        <CardHeader\n          // @ts-ignore\n          ref={ref}\n          className=\"absolute bottom-0 z-10 flex grow flex-col justify-center rounded-b-none p-0\"\n        >\n          <div className={backgroundStyles({ content: true })} />\n          <div className=\"grid w-full max-w-[1920px] grid-cols-[1fr_2fr] grid-rows-[1fr_auto_auto] items-stretch justify-center gap-x-4 gap-y-6 px-3 pb-8 pt-5 grid-areas-small sm:grid-rows-[auto_1fr_auto] sm:px-3.5 sm:grid-areas-wide xl:px-4 2xl:px-5\">\n            <div className=\"flex flex-col items-center justify-center grid-in-image\" ref={imageRef}>\n              {seasonDetail?.poster_path ? (\n                <Image\n                  src={TMDB.posterUrl(seasonDetail?.poster_path)}\n                  alt={seasonDetail?.name}\n                  title={seasonDetail?.name}\n                  classNames={{\n                    wrapper: 'w-full sm:w-3/4 xl:w-1/2',\n                    img: 'aspect-[2/3] !min-h-[auto] !min-w-[auto] shadow-xl shadow-default',\n                  }}\n                  placeholder=\"empty\"\n                  responsive={[\n                    {\n                      size: {\n                        width: Math.round(\n                          (imageSize?.width || 0) *\n                            (!isXl && !isSm ? 0.5 : isXl && !isSm ? 0.75 : isXl && isSm ? 1 : 1),\n                        ),\n                        height: Math.round(\n                          ((imageSize?.width || 0) *\n                            3 *\n                            (!isXl && !isSm ? 0.5 : isXl && !isSm ? 0.75 : isXl && isSm ? 1 : 1)) /\n                            2,\n                        ),\n                      },\n                    },\n                  ]}\n                  options={{\n                    contentType: MimeType.WEBP,\n                  }}\n                />\n              ) : (\n                <div className=\"flex w-full items-center justify-center\">\n                  <div className=\"z-0 flex aspect-[2/3] h-auto w-full items-center justify-center rounded-large bg-default sm:w-3/4 xl:w-1/2\">\n                    <PhotoIcon width={36} height={36} />\n                  </div>\n                </div>\n              )}\n            </div>\n            <div className=\"z-50 flex w-full flex-col items-start justify-start grid-in-title\">\n              <h2>\n                {detail?.name} {seasonDetail?.name}\n              </h2>\n              <h5>\n                {seasonDetail?.episodes?.length || 0} {t('episodes')} &middot;{' '}\n                {seasonDetail?.air_date}{' '}\n              </h5>\n            </div>\n            {seasonDetail?.overview ? (\n              <div className=\"flex flex-col gap-y-3 grid-in-info sm:gap-y-6\">\n                <h6>{seasonDetail.overview}</h6>\n              </div>\n            ) : null}\n          </div>\n        </CardHeader>\n        <CardBody\n          style={{\n            // @ts-ignore\n            '--theme-movie-brand': isHydrated ? backgroundColor : 'transparent',\n          }}\n          className=\"absolute bottom-0 p-0 after:absolute after:bottom-0 after:h-full after:w-full after:bg-gradient-to-t after:from-movie-brand-color after:to-transparent after:content-['']\"\n        >\n          <Image\n            src={\n              seasonDetail?.poster_path\n                ? TMDB.posterUrl(seasonDetail?.poster_path, 'w342')\n                : BackgroundDefault\n            }\n            radius=\"none\"\n            classNames={{\n              wrapper: 'w-full h-auto object-cover max-w-full',\n              img: `left-0 top-0 z-0 m-0 object-cover !opacity-30 blur-2xl ${\n                size ? 'visible' : 'invisible'\n              }'}`,\n            }}\n            title={seasonDetail?.name}\n            alt={seasonDetail?.name}\n            placeholder=\"empty\"\n            responsive={[\n              {\n                size: {\n                  width: Math.round(size?.width || 0),\n                  height: Math.round(size?.height || 0) + 72,\n                },\n              },\n            ]}\n            options={{\n              blurRadius: 80,\n              contentType: MimeType.WEBP,\n            }}\n            style={{\n              width: Math.round(size?.width || 0),\n              height: Math.round(size?.height || 0) + 72,\n            }}\n          />\n        </CardBody>\n      </Card>\n      <div className=\"flex w-full flex-col items-center justify-center\">\n        <motion.div\n          className=\"sticky top-[61px] z-[1000] flex w-full justify-center transition-[padding] duration-100 ease-in-out\"\n          style={{\n            backgroundColor: isHydrated ? backgroundColor : 'transparent',\n            paddingTop,\n            paddingBottom,\n          }}\n          ref={tabLinkRef}\n        >\n          <div\n            className={backgroundStyles({ tablink: true })}\n            style={{ backgroundColor: isHydrated ? backgroundColor : 'transparent' }}\n          />\n          <TabLink pages={tvSeasonDetailPages} linkTo={`/tv-shows/${tvId}/season/${seasonId}`} />\n        </motion.div>\n        <Outlet />\n      </div>\n    </>\n  );\n};\n\nexport function ErrorBoundary() {\n  return (\n    <ErrorBoundaryView\n      statusHandlers={{\n        404: ({ params }) => <p>There is no series with the ID: {params.tvId}</p>,\n      }}\n    />\n  );\n}\n\nexport default TvSeasonDetail;\n"
  },
  {
    "path": "app/routes/tv-shows+/$tvId_.season.$seasonId_.episode.$episodeId.watch.tsx",
    "content": "import type { IMovieInfo, ISource } from '@consumet/extensions';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport Vibrant from 'node-vibrant';\n\nimport type { Handle } from '~/types/handle';\nimport {\n  getKissKhEpisodeStream,\n  getKissKhEpisodeSubtitle,\n  getKissKhInfo,\n} from '~/services/kisskh/kisskh.server';\nimport { loklokGetTvEpInfo } from '~/services/loklok';\nimport { LOKLOK_URL } from '~/services/loklok/utils.server';\nimport getProviderList from '~/services/provider.server';\nimport { authenticate, insertHistory } from '~/services/supabase';\nimport {\n  getImdbRating,\n  getInfoWithProvider,\n  getRecommendation,\n  getTvSeasonDetail,\n  getTvShowDetail,\n  getTvShowIMDBId,\n  getWatchEpisode,\n} from '~/services/tmdb/tmdb.server';\nimport type { ISeasonDetail, ITvShowDetail } from '~/services/tmdb/tmdb.types';\nimport { TMDB as TmdbUtils } from '~/services/tmdb/utils.server';\nimport TMDB from '~/utils/media';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\nimport ErrorBoundaryView from '~/components/elements/shared/ErrorBoundaryView';\nimport WatchDetail from '~/components/elements/shared/WatchDetail';\n\nexport const meta = mergeMeta<typeof loader>(({ data, params }) => {\n  if (!data) {\n    return [\n      { title: 'Missing Episode' },\n      {\n        name: 'description',\n        content: `This season of tv show doesn't have episode ${\n          params.episodeId || ''\n        } or this episode is unavailable`,\n      },\n    ];\n  }\n\n  const { detail, seasonDetail } = data || {};\n  return [\n    {\n      title: `Sora - Watch ${detail?.name || ''} season ${params.seasonId || ''} episode ${\n        params.episodeId || ''\n      }`,\n    },\n    { name: 'description', content: seasonDetail?.overview || detail?.overview || '' },\n    {\n      name: 'keywords',\n      content: `Watch ${detail?.name || ''} season ${params.seasonId || ''} episode ${\n        params.episodeId || ''\n      }, Stream ${detail?.name || ''} season ${params.seasonId || ''} episode ${\n        params.episodeId || ''\n      }, Watch ${detail?.name || ''} season ${params.seasonId || ''} episode ${\n        params.episodeId || ''\n      } HD, Online ${detail?.name || ''} season ${params.seasonId || ''} episode ${\n        params.episodeId || ''\n      }, Streaming ${detail?.name || ''} season ${params.seasonId || ''} episode ${\n        params.episodeId || ''\n      }, English, Subtitle ${detail?.name || ''} season ${params.seasonId || ''} episode ${\n        params.episodeId || ''\n      }, English Subtitle`,\n    },\n    {\n      property: 'og:url',\n      content: `https://sorachill.vercel.app/tv-shows/${params.tvId}/season/${params.seasonId}/episode/${params.episodeId}/watch`,\n    },\n    {\n      property: 'og:title',\n      content: `Sora - Watch ${detail?.name || ''} season ${params.seasonId || ''} episode ${\n        params.episodeId || ''\n      }`,\n    },\n    { property: 'og:description', content: seasonDetail?.overview || detail?.overview || '' },\n    {\n      name: 'twitter:title',\n      content: `Sora - Watch ${detail?.name || ''} season ${params.seasonId || ''} episode ${\n        params.episodeId || ''\n      }`,\n    },\n    { name: 'twitter:description', content: seasonDetail?.overview || detail?.overview || '' },\n  ];\n});\n\nconst checkHasNextEpisode = (\n  currentEpisode: number,\n  totalEpisodes: number,\n  totalProviderEpisodes: number,\n) => {\n  if (totalEpisodes > currentEpisode) {\n    return totalProviderEpisodes > currentEpisode;\n  }\n};\n\nexport const loader = async ({ request, params }: LoaderFunctionArgs) => {\n  const user = await authenticate(request, true, true, true);\n\n  const url = new URL(request.url);\n  const provider = url.searchParams.get('provider');\n  const idProvider = url.searchParams.get('id');\n  const routePlayer = `${url.pathname}${url.search}`;\n  const { tvId, seasonId, episodeId } = params;\n  const tid = Number(tvId);\n  const sid = Number(seasonId);\n  const eid = Number(episodeId);\n  if (!tvId || !seasonId || !episodeId || !provider || !idProvider)\n    throw new Response('Not Found', { status: 404 });\n\n  const [detail, imdbId, recommendations, seasonDetail] = await Promise.all([\n    getTvShowDetail(tid),\n    getTvShowIMDBId(tid),\n    getRecommendation('tv', tid),\n    getTvSeasonDetail(tid, sid),\n  ]);\n  if (!imdbId || !detail) throw new Response('Not Found', { status: 404 });\n  const totalEpisodes = Number(seasonDetail?.episodes.length);\n  const title = detail?.name || '';\n  const orgTitle = detail?.original_name || '';\n  const year = new Date(seasonDetail?.air_date || '').getFullYear();\n  const season = seasonDetail?.season_number;\n  const titlePlayer = detail?.name || detail?.original_name || '';\n  const posterPlayer = TmdbUtils.backdropUrl(detail?.backdrop_path || '', 'w1280');\n  const subtitleOptions = {\n    parent_tmdb_id: detail?.id,\n    season_number: sid,\n    episode_number: eid,\n    type: 'episode',\n    title: detail?.name,\n    sub_format: provider === 'KissKh' ? 'srt' : 'webvtt',\n  };\n  const overview = detail?.overview || undefined;\n  const extractColorImageUrl =\n    process.env.NODE_ENV === 'development'\n      ? TMDB.backdropUrl(detail?.backdrop_path || detail?.poster_path || '', 'w300')\n      : `https://corsproxy.io/?${encodeURIComponent(\n          TMDB.backdropUrl(detail?.backdrop_path || detail?.poster_path || '', 'w300'),\n        )}`;\n  const isEnded = detail?.status === 'Ended' || detail?.status === 'Canceled';\n\n  if (user) {\n    insertHistory({\n      user_id: user.id,\n      media_type: 'movie',\n      duration: (detail?.episode_run_time?.[0] || 0) * 60,\n      watched: 0,\n      route: url.pathname + url.search,\n      media_id: (detail?.id || tid).toString(),\n      poster: TMDB.backdropUrl(detail?.backdrop_path || '', 'w300'),\n      title: detail?.name || detail?.original_name || undefined,\n      overview: detail?.overview || undefined,\n      season: seasonId,\n      episode: episodeId,\n    });\n  }\n\n  if (provider === 'Loklok') {\n    if (!idProvider) throw new Response('Id Not Found', { status: 404 });\n\n    const [tvDetail, imdbRating, providers, fimg] = await Promise.all([\n      loklokGetTvEpInfo(idProvider, Number(episodeId) - 1),\n      imdbId && process.env.IMDB_API_URL !== undefined ? getImdbRating(imdbId) : undefined,\n      getProviderList({\n        type: 'tv',\n        title,\n        orgTitle,\n        year,\n        season,\n        animeId: undefined,\n        animeType: undefined,\n        isEnded,\n        tmdbId: detail?.id,\n      }),\n      fetch(extractColorImageUrl),\n    ]);\n    const totalProviderEpisodes = Number(tvDetail?.data?.episodeCount);\n    const hasNextEpisode = checkHasNextEpisode(eid, totalEpisodes, totalProviderEpisodes);\n    const fimgb = Buffer.from(await fimg.arrayBuffer());\n    const palette =\n      (detail?.backdrop_path || detail?.poster_path) && fimgb\n        ? await Vibrant.from(fimgb).getPalette()\n        : undefined;\n    return json(\n      {\n        idProvider,\n        provider,\n        detail,\n        imdbId,\n        recommendations,\n        imdbRating,\n        seasonDetail,\n        hasNextEpisode,\n        sources: tvDetail?.sources,\n        subtitles: tvDetail?.subtitles.map((sub) => ({\n          lang: `${sub.language} (${sub.lang})`,\n          url: `${LOKLOK_URL}/subtitle?url=${sub.url}`,\n        })),\n        userId: user?.id,\n        providers,\n        routePlayer,\n        titlePlayer,\n        id: tid,\n        posterPlayer,\n        typeVideo: 'tv',\n        subtitleOptions,\n        overview,\n        color: palette\n          ? Object.values(palette).sort((a, b) =>\n              a?.population === undefined || b?.population === undefined\n                ? 0\n                : b.population - a.population,\n            )[0]?.hex\n          : undefined,\n      },\n      {\n        headers: {\n          'Cache-Control': CACHE_CONTROL.watch,\n        },\n      },\n    );\n  }\n\n  if (provider === 'Flixhq') {\n    if (!idProvider) throw new Response('Id Not Found', { status: 404 });\n    const [tvDetail, imdbRating, providers, fimg] = await Promise.all([\n      getInfoWithProvider(tid.toString(), 'tv'),\n      imdbId && process.env.IMDB_API_URL !== undefined ? getImdbRating(imdbId) : undefined,\n      getProviderList({\n        type: 'tv',\n        title,\n        orgTitle,\n        year,\n        season,\n        animeId: undefined,\n        animeType: undefined,\n        isEnded,\n        tmdbId: detail?.id,\n      }),\n      fetch(extractColorImageUrl),\n    ]);\n    const findTvSeason = (tvDetail as IMovieInfo)?.seasons?.find(\n      (s) => s.season === Number(season),\n    );\n    const totalProviderEpisodes = findTvSeason?.episodes.filter((e) => e.id).length;\n    const hasNextEpisode = checkHasNextEpisode(eid, totalEpisodes, totalProviderEpisodes || 0);\n    const tvEpisodeDetail = findTvSeason?.episodes?.find(\n      (episode) => episode.season === Number(seasonId) && episode.episode === Number(episodeId),\n    );\n    const fimgb = Buffer.from(await fimg.arrayBuffer());\n    if (tvEpisodeDetail) {\n      const [tvEpisodeStreamLink, palette] = await Promise.all([\n        getWatchEpisode(tid.toString(), tvEpisodeDetail.id),\n        detail?.backdrop_path || detail?.poster_path\n          ? await Vibrant.from(fimgb).getPalette()\n          : undefined,\n      ]);\n      return json(\n        {\n          provider,\n          idProvider,\n          detail,\n          imdbId,\n          recommendations,\n          imdbRating,\n          seasonDetail,\n          hasNextEpisode,\n          data: tvDetail,\n          sources: (tvEpisodeStreamLink as ISource)?.sources,\n          subtitles: (tvEpisodeStreamLink as ISource)?.subtitles,\n          userId: user?.id,\n          providers,\n          routePlayer,\n          titlePlayer,\n          id: tid,\n          posterPlayer,\n          typeVideo: 'tv',\n          subtitleOptions,\n          overview,\n          color: palette\n            ? Object.values(palette).sort((a, b) =>\n                a?.population === undefined || b?.population === undefined\n                  ? 0\n                  : b.population - a.population,\n              )[0]?.hex\n            : undefined,\n        },\n        {\n          headers: {\n            'Cache-Control': CACHE_CONTROL.watch,\n          },\n        },\n      );\n    }\n  }\n\n  if (provider === 'KissKh') {\n    if (!idProvider) throw new Response('Id Not Found', { status: 404 });\n\n    const [episodeDetail, imdbRating, providers, fimg] = await Promise.all([\n      getKissKhInfo(Number(idProvider)),\n      imdbId && process.env.IMDB_API_URL !== undefined ? getImdbRating(imdbId) : undefined,\n      getProviderList({\n        type: 'tv',\n        title,\n        orgTitle,\n        year,\n        season,\n        animeId: undefined,\n        animeType: undefined,\n        isEnded,\n        tmdbId: detail?.id,\n      }),\n      fetch(extractColorImageUrl),\n    ]);\n    const totalProviderEpisodes = Number(episodeDetail?.episodes?.length);\n    const hasNextEpisode = checkHasNextEpisode(eid, totalEpisodes, totalProviderEpisodes);\n    const episodeSearch = episodeDetail?.episodes?.find((e) => e?.number === Number(episodeId));\n    const fimgb = Buffer.from(await fimg.arrayBuffer());\n    const [episodeStream, episodeSubtitle, palette] = await Promise.all([\n      getKissKhEpisodeStream(Number(episodeSearch?.id)),\n      episodeSearch && episodeSearch.sub > 0\n        ? getKissKhEpisodeSubtitle(Number(episodeSearch?.id))\n        : undefined,\n      detail?.backdrop_path || detail?.poster_path\n        ? await Vibrant.from(fimgb).getPalette()\n        : undefined,\n    ]);\n\n    return json(\n      {\n        provider,\n        idProvider,\n        detail,\n        imdbId,\n        recommendations,\n        imdbRating,\n        seasonDetail,\n        hasNextEpisode,\n        sources: [{ url: episodeStream?.Video || '', isM3U8: true, quality: 'auto' }],\n        subtitles: episodeSubtitle?.map((sub) => ({\n          lang: sub.label,\n          url: sub.src,\n          ...(sub.default && { default: true }),\n        })),\n        userId: user?.id,\n        providers,\n        routePlayer,\n        titlePlayer,\n        id: tid,\n        posterPlayer,\n        typeVideo: 'tv',\n        subtitleOptions,\n        overview,\n        color: palette\n          ? Object.values(palette).sort((a, b) =>\n              a?.population === undefined || b?.population === undefined\n                ? 0\n                : b.population - a.population,\n            )[0]?.hex\n          : undefined,\n      },\n      {\n        headers: {\n          'Cache-Control': CACHE_CONTROL.watch,\n        },\n      },\n    );\n  }\n\n  const [imdbRating, providers, fimg] = await Promise.all([\n    imdbId && process.env.IMDB_API_URL !== undefined ? getImdbRating(imdbId) : undefined,\n    getProviderList({\n      type: 'tv',\n      title,\n      orgTitle,\n      year,\n      season,\n      animeId: undefined,\n      animeType: undefined,\n      isEnded,\n      tmdbId: detail?.id,\n    }),\n    fetch(extractColorImageUrl),\n  ]);\n\n  const fimgb = Buffer.from(await fimg.arrayBuffer());\n  const palette =\n    detail?.backdrop_path || detail?.poster_path\n      ? await Vibrant.from(fimgb).getPalette()\n      : undefined;\n  return json({\n    idProvider,\n    provider,\n    detail,\n    imdbId,\n    recommendations,\n    imdbRating,\n    seasonDetail,\n    providers,\n    userId: user?.id,\n    routePlayer,\n    titlePlayer,\n    id: tid,\n    posterPlayer,\n    typeVideo: 'tv',\n    subtitleOptions,\n    overview,\n    color: palette\n      ? Object.values(palette).sort((a, b) =>\n          a?.population === undefined || b?.population === undefined\n            ? 0\n            : b.population - a.population,\n        )[0]?.hex\n      : undefined,\n  });\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ match, t }) => (\n    <>\n      <BreadcrumbItem\n        to={`/tv-shows/${match.params.tvId}/`}\n        key={`tv-shows-${match.params.tvId}-overview`}\n      >\n        {(match.data as { detail: ITvShowDetail })?.detail?.name ||\n          (match.data as { detail: ITvShowDetail })?.detail?.original_name ||\n          match.params.tvId}\n      </BreadcrumbItem>\n      <BreadcrumbItem\n        to={`/tv-shows/${match.params.tvId}/season/${match.params.seasonId}/`}\n        key={`tv-shows-${match.params.tvId}-season-${match.params.seasonId}-episodes`}\n      >\n        {t('season')} {match.params.seasonId}\n      </BreadcrumbItem>\n      <BreadcrumbItem\n        to={`/tv-shows/${match.params.tvId}/season/${match.params.seasonId}/episode/${match.params.episodeId}`}\n        key={`tv-shows-${match.params.tvId}-season-${match.params.seasonId}-episode-${match.params.episodeId}`}\n      >\n        {t('episode')} {match.params.episodeId}\n      </BreadcrumbItem>\n    </>\n  ),\n  playerSettings: {\n    isMini: false,\n    shouldShowPlayer: true,\n  },\n  miniTitle: ({ match, t }) => ({\n    title:\n      (match.data as { detail: ITvShowDetail })?.detail?.name ||\n      (match.data as { detail: ITvShowDetail })?.detail?.original_name ||\n      '',\n    subtitle: `${(match.data as { seasonDetail: ISeasonDetail })?.seasonDetail?.name} ${t(\n      'episode',\n    )} ${match.params.episodeId}`,\n    showImage:\n      (match.data as { seasonDetail: ISeasonDetail })?.seasonDetail?.poster_path !== undefined,\n    imageUrl: TMDB.posterUrl(\n      (match.data as { seasonDetail: ISeasonDetail })?.seasonDetail?.poster_path || '',\n      'w92',\n    ),\n  }),\n};\n\nconst TvEpisodeWatch = () => {\n  const { detail, recommendations, imdbRating, seasonDetail, providers, color } =\n    useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const id = detail && detail.id;\n  return (\n    <div className=\"mt-3 flex w-full flex-col items-center justify-center px-3 sm:px-0\">\n      <WatchDetail\n        id={Number(id)}\n        type=\"tv\"\n        title={detail?.name || ''}\n        // @ts-ignore\n        episodes={seasonDetail?.episodes}\n        overview={detail?.overview || ''}\n        posterPath={detail?.poster_path ? TMDB.posterUrl(detail?.poster_path, 'w342') : undefined}\n        tmdbRating={detail?.vote_average}\n        imdbRating={imdbRating?.star}\n        genresMedia={detail?.genres}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        recommendationsMovies={recommendations?.items}\n        season={seasonDetail?.season_number}\n        providers={providers}\n        color={color}\n      />\n    </div>\n  );\n};\n\nexport function ErrorBoundary() {\n  return (\n    <ErrorBoundaryView\n      statusHandlers={{\n        404: ({ params }) => <p>This series doesn't have episode {params.episodeId}</p>,\n      }}\n    />\n  );\n}\n\nexport default TvEpisodeWatch;\n"
  },
  {
    "path": "app/routes/tv-shows+/_index.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport dayjs from 'dayjs';\nimport { motion } from 'framer-motion';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getListDiscover, getListTvShows, getTrending } from '~/services/tmdb/tmdb.server';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Tv Shows' },\n  { name: 'description', content: 'Discover tv shows in Sora' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/tv-shows/' },\n  { property: 'og:title', content: 'Sora - Tv Shows' },\n  { property: 'og:description', content: 'Discover tv shows in Sora' },\n  { name: 'twitter:title', content: 'Sora - Tv Shows' },\n  { name: 'twitter:description', content: 'Discover tv shows in Sora' },\n]);\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const page = 1;\n  const today = dayjs();\n  // get next 7 days\n  const next7Days = today.add(7, 'day');\n  const formattedToday = today.format('YYYY-MM-DD');\n  const formattedNext7Days = next7Days.format('YYYY-MM-DD');\n  const [trending, popular, airingToday, onTheAir, topRated] = await Promise.all([\n    getTrending('tv', 'day', locale, page),\n    getListDiscover(\n      'tv',\n      undefined,\n      undefined,\n      locale,\n      page,\n      undefined,\n      undefined,\n      undefined,\n      undefined,\n      undefined,\n      100,\n    ),\n    getListTvShows('airing_today', locale, page),\n    getListDiscover(\n      'tv',\n      undefined,\n      undefined,\n      locale,\n      page,\n      undefined,\n      undefined,\n      undefined,\n      undefined,\n      undefined,\n      100,\n      undefined,\n      undefined,\n      undefined,\n      undefined,\n      undefined,\n      undefined,\n      undefined,\n      undefined,\n      undefined,\n      undefined,\n      undefined,\n      undefined,\n      formattedToday,\n      formattedNext7Days,\n    ),\n    getListTvShows('top_rated', locale, page),\n  ]);\n  return json(\n    {\n      trending,\n      popular,\n      airingToday,\n      onTheAir,\n      topRated,\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.tv,\n      },\n    },\n  );\n};\n\nexport const handle: Handle = {\n  disableLayoutPadding: true,\n  miniTitle: ({ t }) => ({\n    title: t('tv-shows'),\n    showImage: false,\n  }),\n};\n\nconst TvIndexPage = () => {\n  const { trending, popular, airingToday, onTheAir, topRated } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const location = useLocation();\n  const navigate = useNavigate();\n  const { t } = useTranslation();\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ x: '-10%', opacity: 0 }}\n      animate={{ x: '0', opacity: 1 }}\n      exit={{ y: '-10%', opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      style={{\n        width: '100%',\n        display: 'flex',\n        justifyContent: 'center',\n        flexDirection: 'column',\n        alignItems: 'center',\n      }}\n    >\n      <MediaList\n        listType=\"slider-banner\"\n        items={trending.items}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n      />\n      <div className=\"mt-9 flex w-full flex-col items-center justify-start px-3 sm:px-5\">\n        {popular?.items && popular.items.length > 0 && (\n          <MediaList\n            genresMovie={rootData?.genresMovie}\n            genresTv={rootData?.genresTv}\n            items={popular.items}\n            itemsType=\"tv\"\n            listName={t('popular-tv-shows')}\n            listType=\"slider-card\"\n            navigationButtons\n            onClickViewMore={() => navigate('/tv-shows/popular')}\n            showMoreList\n          />\n        )}\n        {airingToday?.items && airingToday.items.length > 0 && (\n          <MediaList\n            genresMovie={rootData?.genresMovie}\n            genresTv={rootData?.genresTv}\n            items={airingToday.items}\n            itemsType=\"tv\"\n            listName={t('airing-today-tv-shows')}\n            listType=\"slider-card\"\n            navigationButtons\n            onClickViewMore={() => navigate('/tv-shows/airing-today')}\n            showMoreList\n          />\n        )}\n        {onTheAir?.items && onTheAir.items.length > 0 && (\n          <MediaList\n            genresMovie={rootData?.genresMovie}\n            genresTv={rootData?.genresTv}\n            items={onTheAir.items}\n            itemsType=\"tv\"\n            listName={t('on-the-air-tv-shows')}\n            listType=\"slider-card\"\n            navigationButtons\n            onClickViewMore={() => navigate('/tv-shows/on-the-air')}\n            showMoreList\n          />\n        )}\n        {topRated?.items && topRated.items.length > 0 && (\n          <MediaList\n            genresMovie={rootData?.genresMovie}\n            genresTv={rootData?.genresTv}\n            items={topRated.items}\n            itemsType=\"tv\"\n            listName={t('top-rated-tv-shows')}\n            listType=\"slider-card\"\n            navigationButtons\n            onClickViewMore={() => navigate('/tv-shows/top-rated')}\n            showMoreList\n          />\n        )}\n      </div>\n    </motion.div>\n  );\n};\n\nexport default TvIndexPage;\n"
  },
  {
    "path": "app/routes/tv-shows+/airing-today.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getListTvShows } from '~/services/tmdb/tmdb.server';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Airing Today Tv Shows' },\n  { name: 'description', content: 'Airing Today Tv Shows' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/tv-shows/airing-today' },\n  { property: 'og:title', content: 'Sora - Airing Today Tv Shows' },\n  { property: 'og:description', content: 'Airing Today Tv Shows' },\n  { name: 'twitter:title', content: 'Sora - Airing Today Tv Shows' },\n  { name: 'twitter:description', content: 'Airing Today Tv Shows' },\n]);\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page')) || undefined;\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  return json(\n    {\n      shows: await getListTvShows('airing_today', locale, page),\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.airingToday,\n      },\n    },\n  );\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/tv-shows/airing-today\" key=\"tv-shows-airing-today\">\n      {t('airing-today-tv-shows')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('airing-today-tv-shows'),\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst ListAiringTodayTvShows = () => {\n  const { shows } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const location = useLocation();\n  const navigate = useNavigate();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_e: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate('/tv-shows/popular');\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate('/tv-shows/on-the-air');\n    }\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <MediaList\n        currentPage={shows?.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={shows?.items}\n        itemsType=\"tv\"\n        listName={t('airing-today-tv-shows')}\n        listType=\"grid\"\n        showListTypeChangeButton\n        totalPages={shows?.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default ListAiringTodayTvShows;\n"
  },
  {
    "path": "app/routes/tv-shows+/on-the-air.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport dayjs from 'dayjs';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getListDiscover } from '~/services/tmdb/tmdb.server';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - On air Tv Shows' },\n  { name: 'description', content: 'On air Tv Shows' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/tv-shows/on-the-air' },\n  { property: 'og:title', content: 'Sora - On air Tv Shows' },\n  { property: 'og:description', content: 'On air Tv Shows' },\n  { name: 'twitter:title', content: 'Sora - On air Tv Shows' },\n  { name: 'twitter:description', content: 'On air Tv Shows' },\n]);\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page')) || undefined;\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  const today = dayjs();\n  // get next 7 days\n  const next7Days = today.add(7, 'day');\n  const formattedToday = today.format('YYYY-MM-DD');\n  const formattedNext7Days = next7Days.format('YYYY-MM-DD');\n\n  return json(\n    {\n      shows: await getListDiscover(\n        'tv',\n        undefined,\n        undefined,\n        locale,\n        page,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        100,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        formattedToday,\n        formattedNext7Days,\n      ),\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.trending,\n      },\n    },\n  );\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/tv-shows/on-the-air\" key=\"tv-shows-on-air\">\n      {t('on-the-air-tv-shows')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('on-the-air-tv-shows'),\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst ListOnAirTvShows = () => {\n  const { shows } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const location = useLocation();\n  const navigate = useNavigate();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_e: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate('/tv-shows/airing-today');\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate('/tv-shows/top-rated');\n    }\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <MediaList\n        currentPage={shows?.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={shows?.items}\n        itemsType=\"tv\"\n        listName={t('on-the-air-tv-shows')}\n        listType=\"grid\"\n        showListTypeChangeButton\n        totalPages={shows?.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default ListOnAirTvShows;\n"
  },
  {
    "path": "app/routes/tv-shows+/popular.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getListDiscover } from '~/services/tmdb/tmdb.server';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Popular Tv Shows' },\n  { name: 'description', content: 'Popular Tv Shows' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/tv-shows/popular' },\n  { property: 'og:title', content: 'Sora - Popular Tv Shows' },\n  { property: 'og:description', content: 'Popular Tv Shows' },\n  { name: 'twitter:title', content: 'Sora - Popular Tv Shows' },\n  { name: 'twitter:description', content: 'Popular Tv Shows' },\n]);\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page')) || undefined;\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  return json(\n    {\n      shows: await getListDiscover(\n        'tv',\n        undefined,\n        undefined,\n        locale,\n        page,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        100,\n      ),\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.popular,\n      },\n    },\n  );\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/tv-shows/popular\" key=\"tv-shows-popular\">\n      {t('popular-tv-shows')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('popular-tv-shows'),\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst ListTvShows = () => {\n  const { shows } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const location = useLocation();\n  const navigate = useNavigate();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_e: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      return;\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      navigate('/tv-shows/airing-today');\n    }\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <MediaList\n        currentPage={shows?.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={shows?.items}\n        itemsType=\"tv\"\n        listName={t('popular-tv-shows')}\n        listType=\"grid\"\n        showListTypeChangeButton\n        totalPages={shows?.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default ListTvShows;\n"
  },
  {
    "path": "app/routes/tv-shows+/top-rated.tsx",
    "content": "import { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\nimport { motion, type PanInfo } from 'framer-motion';\nimport { isMobile } from 'react-device-detect';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { i18next } from '~/services/i18n';\nimport { authenticate } from '~/services/supabase';\nimport { getListTvShows } from '~/services/tmdb/tmdb.server';\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\nimport { useTypedRouteLoaderData } from '~/utils/react/hooks/useTypedRouteLoaderData';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport MediaList from '~/components/media/MediaList';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Top Rated Tv Shows' },\n  { name: 'description', content: 'Top Rated Tv Shows' },\n  { property: 'og:url', content: 'https://sorachill.vercel.app/tv-shows/top-rated' },\n  { property: 'og:title', content: 'Sora - Top Rated Tv Shows' },\n  { property: 'og:description', content: 'Top Rated Tv Shows' },\n  { name: 'twitter:title', content: 'Sora - Top Rated Tv Shows' },\n  { name: 'twitter:description', content: 'Top Rated Tv Shows' },\n]);\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const [, locale] = await Promise.all([\n    authenticate(request, undefined, true),\n    i18next.getLocale(request),\n  ]);\n\n  const url = new URL(request.url);\n  let page = Number(url.searchParams.get('page')) || undefined;\n  if (page && (page < 1 || page > 1000)) page = 1;\n\n  return json(\n    {\n      shows: await getListTvShows('top_rated', locale, page),\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.topRated,\n      },\n    },\n  );\n};\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/tv-shows/top-rated\" key=\"tv-shows-top-rated\">\n      {t('top-rated-tv-shows')}\n    </BreadcrumbItem>\n  ),\n  miniTitle: ({ t }) => ({\n    title: t('top-rated-tv-shows'),\n    showImage: false,\n  }),\n  showListViewChangeButton: true,\n};\n\nconst ListTvShows = () => {\n  const { shows } = useLoaderData<typeof loader>();\n  const rootData = useTypedRouteLoaderData('root');\n  const location = useLocation();\n  const navigate = useNavigate();\n  const isHydrated = useHydrated();\n  const { t } = useTranslation();\n\n  const handleDragEnd = (_e: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {\n    if (info.offset?.x > 100) {\n      navigate('/tv-shows/on-the-air');\n    }\n    if (info.offset?.x < -100 && info.offset?.y > -50) {\n      return;\n    }\n  };\n\n  return (\n    <motion.div\n      key={location.key}\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.3 }}\n      className=\"flex w-full flex-col items-center justify-center px-3 sm:px-0\"\n      drag={isMobile && isHydrated ? 'x' : false}\n      dragConstraints={isMobile && isHydrated ? { left: 0, right: 0 } : false}\n      dragElastic={isMobile && isHydrated ? 0.7 : false}\n      onDragEnd={handleDragEnd}\n      dragDirectionLock={isMobile && isHydrated}\n      draggable={isMobile && isHydrated}\n    >\n      <MediaList\n        currentPage={shows?.page}\n        genresMovie={rootData?.genresMovie}\n        genresTv={rootData?.genresTv}\n        items={shows?.items}\n        itemsType=\"tv\"\n        listName={t('top-rated-tv-shows')}\n        listType=\"grid\"\n        showListTypeChangeButton\n        totalPages={shows?.totalPages}\n      />\n    </motion.div>\n  );\n};\n\nexport default ListTvShows;\n"
  },
  {
    "path": "app/routes/tv-shows.tsx",
    "content": "import { Outlet } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport { tvPages } from '~/constants/tabLinks';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const meta = mergeMeta(() => [\n  {\n    name: 'keywords',\n    content:\n      'watch free tv shows, free tv shows to watch online, watch tv shows online free, free tv shows streaming, free tv shows full, free tv shows download, watch tv shows hd, tv shows to watch, hd tv shows, stream tv shows, tv shows to stream, watch tv shows free',\n  },\n  { property: 'og:image', content: 'https://sorachill.vercel.app/api/ogimage?it=tvshows' },\n  { name: 'twitter:image', content: 'https://sorachill.vercel.app/api/ogimage?it=tvshows' },\n]);\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/tv-shows\" key=\"tv-shows\">\n      {t('tv-shows')}\n    </BreadcrumbItem>\n  ),\n  showTabLink: true,\n  tabLinkPages: tvPages,\n  tabLinkTo: () => '/tv-shows',\n  hideTabLinkWithLocation: (locationPathname: string) => {\n    if (locationPathname.split('/')[2]?.match(/^\\d+$/) || locationPathname === '/tv-shows')\n      return true;\n    return false;\n  },\n};\n\nconst TvPage = () => <Outlet />;\n\nexport default TvPage;\n"
  },
  {
    "path": "app/routes/watch-history.tsx",
    "content": "import { useState } from 'react';\nimport { Button } from '@nextui-org/button';\nimport { Checkbox, CheckboxGroup } from '@nextui-org/checkbox';\nimport { Input } from '@nextui-org/input';\nimport { Pagination } from '@nextui-org/pagination';\nimport { useMediaQuery } from '@react-hookz/web';\nimport { json, type LoaderFunctionArgs } from '@remix-run/node';\nimport { useLoaderData, useLocation, useNavigate } from '@remix-run/react';\nimport { mergeMeta } from '~/utils';\n\nimport type { Handle } from '~/types/handle';\nimport { authenticate, getCountHistory, getHistory, type IHistory } from '~/services/supabase';\nimport { CACHE_CONTROL } from '~/utils/server/http';\nimport HistoryItem from '~/components/media/item/HistoryItem';\nimport { BreadcrumbItem } from '~/components/elements/Breadcrumb';\n\nexport const handle: Handle = {\n  breadcrumb: ({ t }) => (\n    <BreadcrumbItem to=\"/watch-history\" key=\"watch-history\">\n      {t('watch-history')}\n    </BreadcrumbItem>\n  ),\n  getSitemapEntries: () => null,\n  miniTitle: ({ t }) => ({\n    title: t('watch-history'),\n    showImage: false,\n  }),\n};\n\nexport const meta = mergeMeta(() => [\n  { title: 'Sora - Watch History' },\n  { name: 'description', content: 'Watch History' },\n  { property: 'og:title', content: 'Sora - Watch History' },\n  { property: 'og:description', content: 'Watch History' },\n  { name: 'twitter:title', content: 'Sora - Watch History' },\n  { name: 'twitter:description', content: 'Watch History' },\n]);\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n  const user = await authenticate(request, true, true);\n\n  const { searchParams } = new URL(request.url);\n  const page = Number(searchParams.get('page')) || 1;\n  const types = searchParams.get('types');\n  const from = searchParams.get('from');\n  const to = searchParams.get('to');\n\n  return json(\n    {\n      histories: user ? await getHistory(user.id, types, from, to, page) : [],\n      totalPage: user ? Math.ceil((await getCountHistory(user.id, types, from, to)) / 20) : 0,\n      page,\n    },\n    {\n      headers: {\n        'Cache-Control': CACHE_CONTROL.default,\n      },\n    },\n  );\n};\n\nconst History = () => {\n  const { histories, page, totalPage } = useLoaderData<typeof loader>();\n  const isSm = useMediaQuery('(max-width: 650px)', { initializeWithValue: false });\n  const navigate = useNavigate();\n  const location = useLocation();\n\n  const sParams = new URLSearchParams(location.search);\n\n  const [types, setTypes] = useState<string[]>(sParams.get('types')?.split(',') || []);\n  const [from, setFrom] = useState<string | undefined>(sParams.get('from') || '');\n  const [to, setTo] = useState<string | undefined>(sParams.get('to') || '');\n\n  const searchHistoryHandler = () => {\n    const params = new URLSearchParams();\n    if ([1, 2].includes(types?.length)) params.append('types', types?.join(','));\n    if (from) params.append('from', from);\n    if (to) params.append('to', to);\n    navigate(`/watch-history?${params.toString()}`);\n  };\n\n  const paginationChangeHandler = (_page: number) => {\n    const url = new URL(document.URL);\n    url.searchParams.set('page', _page.toString());\n    navigate(`${url.pathname}${url.search}`);\n  };\n\n  return (\n    <div className=\"flex w-full flex-col justify-start gap-6 px-3 sm:px-0\">\n      <h2>Your watch history</h2>\n      <div className=\"flex flex-row flex-wrap items-center justify-start gap-6\">\n        <CheckboxGroup\n          label=\"Select media types\"\n          orientation=\"horizontal\"\n          color=\"primary\"\n          defaultValue={types}\n          onValueChange={setTypes}\n        >\n          <Checkbox value=\"movie\">Movie</Checkbox>\n          <Checkbox value=\"tv\">TV Show</Checkbox>\n          <Checkbox value=\"anime\">Anime</Checkbox>\n        </CheckboxGroup>\n        <div className=\"flex gap-x-2\">\n          <Input\n            label=\"From\"\n            type=\"date\"\n            placeholder=\"Enter your date\"\n            value={from || undefined}\n            onValueChange={setFrom}\n          />\n          <Input\n            label=\"To\"\n            type=\"date\"\n            placeholder=\"Enter your date\"\n            value={to || undefined}\n            onValueChange={setTo}\n          />\n        </div>\n      </div>\n      <Button\n        type=\"button\"\n        color=\"primary\"\n        size=\"md\"\n        onPress={searchHistoryHandler}\n        className=\"w-48\"\n      >\n        Search History\n      </Button>\n      <div className=\"grid w-full grid-cols-1 justify-items-center gap-4 xl:grid-cols-2\">\n        {histories.map((item) => (\n          <HistoryItem key={item.id} item={item as unknown as IHistory} />\n        ))}\n      </div>\n      {totalPage > 1 ? (\n        <div className=\"mt-7 flex justify-center\">\n          <Pagination\n            // showControls={!isSm}\n            total={totalPage}\n            initialPage={page}\n            // shadow\n            onChange={paginationChangeHandler}\n            {...(isSm && { size: 'sm' })}\n          />\n        </div>\n      ) : null}\n    </div>\n  );\n};\n\nexport default History;\n"
  },
  {
    "path": "app/services/aniskip/aniskip.server.ts",
    "content": "import { fetcher, lruCache } from '~/utils/server/cache.server';\n\nexport interface IAniSkipResponse {\n  found: boolean;\n  results: IAniskipResults[];\n  message: string;\n  statusCode: number;\n}\n\ninterface IAniskipResults {\n  interval: IInterval;\n  skipType: string;\n  skipId: string;\n  episodeLength: number;\n}\n\ninterface IInterval {\n  startTime: number;\n  endTime: number;\n}\n\nexport const getAniskip = async (\n  id: number,\n  episodeNumber: number,\n  episodeLength?: number,\n): Promise<IAniSkipResponse | undefined> => {\n  try {\n    const url = `https://api.aniskip.com/v2/skip-times/${id}/${episodeNumber}?types[]=ed&types[]=mixed-ed&types[]=mixed-op&types[]=op&types[]=recap&episodeLength=${\n      episodeLength || 0\n    }`;\n    const fetched = await fetcher<IAniSkipResponse>({\n      url,\n      key: `aniskip-${id}-${episodeNumber}`,\n      ttl: 1000 * 60 * 60 * 24 * 7,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 30,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.log(error);\n  }\n};\n"
  },
  {
    "path": "app/services/configs.server.ts",
    "content": "import { env, exit } from 'process';\n\n// server global config\ninterface ISGConfigs {\n  /**\n   * required login for every routes in app\n   * default true\n   */\n  __globalAuthRequired: boolean;\n  /**\n   * required invite code to sign up new account\n   * default true\n   */\n  __invitedSignUpOnly: boolean;\n  /**\n   * invited sign-up key\n   * this key should be changed once in a while\n   */\n  __invitedSignUpKey: string;\n  /**\n   * turn on/off loklok provider\n   * default false for development\n   */\n  __loklokProvider: boolean;\n  /**\n   * turn on/off bilibli provider\n   * default false for development\n   */\n  __bilibiliProvider: boolean;\n  /**\n   * turn on/off kisskh provider\n   * default false for development\n   */\n  __kisskhProvider: boolean;\n}\n\nconst sgConfigs: ISGConfigs = {\n  __globalAuthRequired: env.GLOBAL_AUTH_REQUIRED === 'ON',\n  __invitedSignUpOnly: env.INVITED_SIGNUP_ONLY === 'ON',\n  __invitedSignUpKey: env.INVITED_SIGNUP_KEY || '',\n  __loklokProvider: env.LOKLOK_PROVIDER === 'ON',\n  __bilibiliProvider: env.BILIBILI_PROVIDER === 'ON',\n  __kisskhProvider: env.KISSKH_PROVIDER === 'ON',\n};\n\nif (sgConfigs.__invitedSignUpOnly && !sgConfigs.__invitedSignUpKey) {\n  console.error('Invited key is needed for Invited SignUp Only Mode');\n  exit();\n}\n\nexport default sgConfigs;\n"
  },
  {
    "path": "app/services/consumet/anilist/anilist.server.ts",
    "content": "import { META, PROVIDERS_LIST } from '@consumet/extensions';\n\nimport { cachified, lruCache } from '~/utils/server/cache.server';\n\nimport type { IAnimeInfo, IAnimeList, IAnimeResult, IEpisodeInfo } from './anilist.types';\nimport { fetchAnimeEpisodeHandler, fetchAnimeResultsHandler } from './utils.server';\n\nconst generateAnilistMeta = (provider?: string) => {\n  if (provider) {\n    const possibleProvider = PROVIDERS_LIST.ANIME.find(\n      (p) => p.name.toLowerCase() === provider.toLocaleLowerCase(),\n    );\n    if (!possibleProvider) {\n      throw new Error(`Provider ${provider} not found`);\n    }\n    return new META.Anilist(possibleProvider);\n  }\n  return new META.Anilist();\n};\n\nexport const getAnimeSearch = async (\n  query: string,\n  page?: number,\n  perPage?: number,\n): Promise<IAnimeList | undefined> => {\n  const anilist = generateAnilistMeta();\n  const results = await cachified({\n    key: `anilist-search-${query}-${page}-${perPage}`,\n    ttl: 1000 * 60 * 60,\n    staleWhileRevalidate: 1000 * 60 * 60 * 24,\n    cache: lruCache,\n    request: undefined,\n    getFreshValue: async () => {\n      try {\n        const res = await anilist.search(query, page, perPage);\n        return res;\n      } catch (error) {\n        console.error(error);\n        return {\n          currentPage: 0,\n          hasNextPage: false,\n          results: [],\n        };\n      }\n    },\n  });\n  return {\n    currentPage: results?.currentPage || 0,\n    hasNextPage: results?.hasNextPage || false,\n    results: [...fetchAnimeResultsHandler(results.results as IAnimeResult[])],\n    ...(results.totalPages && { totalPages: results.totalPages }),\n    ...(results.totalResults && { totalResults: results.totalResults }),\n  };\n};\n\nexport const getAnimeRecentEpisodes = async (\n  provider?: 'gogoanime' | 'zoro' | undefined,\n  page?: number,\n  perPage?: number,\n): Promise<IAnimeList | undefined> => {\n  const anilist = generateAnilistMeta(provider);\n  const results = await cachified({\n    key: `anilist-recent-episodes-${page}-${perPage}-${provider}`,\n    ttl: 1000 * 60 * 60 * 24,\n    staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n    cache: lruCache,\n    request: undefined,\n    getFreshValue: async () => {\n      try {\n        const res = await anilist.fetchRecentEpisodes(provider, page, perPage);\n        return res;\n      } catch (error) {\n        console.error(error);\n        return {\n          currentPage: 0,\n          hasNextPage: false,\n          results: [],\n        };\n      }\n    },\n  });\n  return {\n    currentPage: results?.currentPage || 0,\n    hasNextPage: results?.hasNextPage || false,\n    results: [...fetchAnimeEpisodeHandler(results.results as IAnimeResult[])],\n    ...(results.totalPages && { totalPages: results.totalPages }),\n    ...(results.totalResults && { totalResults: results.totalResults }),\n  };\n};\n\nexport const getAnimeAdvancedSearch = async (\n  query?: string,\n  type?: 'ANIME' | 'MANGA',\n  page?: number,\n  perPage?: number,\n  season?: 'WINTER' | 'SPRING' | 'SUMMER' | 'FALL',\n  format?: 'TV' | 'TV_SHORT' | 'OVA' | 'ONA' | 'MOVIE' | 'SPECIAL' | 'MUSIC',\n  sort?: string[],\n  genres?: string[],\n  id?: string,\n  year?: number,\n  status?: 'RELEASING' | 'NOT_YET_RELEASED' | 'FINISHED' | 'CANCELLED' | 'HIATUS',\n): Promise<IAnimeList | undefined> => {\n  console.log('🚀 ~ file: anilist.server.ts:104 ~ sort:', sort);\n  const anilist = generateAnilistMeta();\n  const results = await cachified({\n    key: `anilist-advanced-search-${query}-${type}-${page}-${perPage}-${season}-${format}-${sort}-${genres}-${id}-${year}-${status}`,\n    ttl: 1000 * 60 * 60 * 4,\n    staleWhileRevalidate: 1000 * 60 * 60 * 24 * 2,\n    cache: lruCache,\n    request: undefined,\n    getFreshValue: async () => {\n      try {\n        const res = await anilist.advancedSearch(\n          query,\n          type,\n          page,\n          perPage,\n          format,\n          sort,\n          genres,\n          id,\n          year,\n          status,\n          season,\n        );\n        return res;\n      } catch (error) {\n        console.error(error);\n        return {\n          currentPage: 0,\n          hasNextPage: false,\n          results: [],\n        };\n      }\n    },\n  });\n  return {\n    currentPage: results?.currentPage || 0,\n    hasNextPage: results?.hasNextPage || false,\n    results: [...fetchAnimeResultsHandler(results.results as IAnimeResult[])],\n    ...(results.totalPages && { totalPages: results.totalPages }),\n    ...(results.totalResults && { totalResults: results.totalResults }),\n  };\n};\n\nexport const getAnimeRandom = async (): Promise<IAnimeInfo | undefined> => {\n  const anilist = generateAnilistMeta();\n  try {\n    const res = (await anilist.fetchRandomAnime()) as IAnimeInfo;\n    return {\n      ...res,\n      recommendations: [...fetchAnimeResultsHandler(res.recommendations as IAnimeResult[])],\n    };\n  } catch (error) {\n    console.log(error);\n    return undefined;\n  }\n};\n\nexport const getAnimeTrending = async (\n  page?: number,\n  perPage?: number,\n): Promise<IAnimeList | undefined> => {\n  const anilist = generateAnilistMeta();\n  const results = await cachified({\n    key: `anilist-trending-${page}-${perPage}`,\n    ttl: 1000 * 60 * 60 * 24,\n    staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n    cache: lruCache,\n    request: undefined,\n    getFreshValue: async () => {\n      try {\n        const res = await anilist.fetchTrendingAnime(page, perPage);\n        return res;\n      } catch (error) {\n        console.error(error);\n        return {\n          currentPage: 0,\n          hasNextPage: false,\n          results: [],\n        };\n      }\n    },\n  });\n  return {\n    currentPage: results?.currentPage || 0,\n    hasNextPage: results?.hasNextPage || false,\n    results: [...fetchAnimeResultsHandler(results.results as IAnimeResult[])],\n    ...(results.totalPages && { totalPages: results.totalPages }),\n    ...(results.totalResults && { totalResults: results.totalResults }),\n  };\n};\n\nexport const getAnimePopular = async (\n  page?: number,\n  perPage?: number,\n): Promise<IAnimeList | undefined> => {\n  const anilist = generateAnilistMeta();\n  const results = await cachified({\n    key: `anilist-popular-${page}-${perPage}`,\n    ttl: 1000 * 60 * 60 * 24,\n    staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n    cache: lruCache,\n    request: undefined,\n    getFreshValue: async () => {\n      try {\n        const res = await anilist.fetchPopularAnime(page, perPage);\n        return res;\n      } catch (error) {\n        console.error(error);\n        return {\n          currentPage: 0,\n          hasNextPage: false,\n          results: [],\n        };\n      }\n    },\n  });\n  return {\n    currentPage: results?.currentPage || 0,\n    hasNextPage: results?.hasNextPage || false,\n    results: [...fetchAnimeResultsHandler(results.results as IAnimeResult[])],\n    ...(results.totalPages && { totalPages: results.totalPages }),\n    ...(results.totalResults && { totalResults: results.totalResults }),\n  };\n};\n\nexport const getAnimeGenre = async (\n  genres: string[],\n  page: number,\n  perPage: number,\n): Promise<IAnimeList | undefined> => {\n  const anilist = generateAnilistMeta();\n  const results = await cachified({\n    key: `anilist-genre-${genres}-${page}-${perPage}`,\n    ttl: 1000 * 60 * 60 * 24,\n    staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n    cache: lruCache,\n    request: undefined,\n    getFreshValue: async () => {\n      try {\n        const res = await anilist.fetchAnimeGenres(genres, page, perPage);\n        return res;\n      } catch (error) {\n        console.error(error);\n        return {\n          currentPage: 0,\n          hasNextPage: false,\n          results: [],\n        };\n      }\n    },\n  });\n  return {\n    currentPage: results?.currentPage || 0,\n    hasNextPage: results?.hasNextPage || false,\n    results: [...fetchAnimeResultsHandler(results.results as IAnimeResult[])],\n    ...(results.totalPages && { totalPages: results.totalPages }),\n    ...(results.totalResults && { totalResults: results.totalResults }),\n  };\n};\n\nexport const getAnimeAiringSchedule = async (\n  page?: number,\n  perPage?: number,\n  weekStart?: number | string,\n  weekEnd?: number | string,\n  notYetAired?: boolean,\n): Promise<IAnimeList | undefined> => {\n  const anilist = generateAnilistMeta();\n  const results = await cachified({\n    key: `anilist-airing-schedule-${page}-${perPage}-${weekStart}-${weekEnd}-${notYetAired}`,\n    ttl: 1000 * 60 * 60 * 24,\n    staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n    cache: lruCache,\n    request: undefined,\n    getFreshValue: async () => {\n      try {\n        const res = await anilist.fetchAiringSchedule(\n          page,\n          perPage,\n          weekStart,\n          weekEnd,\n          notYetAired,\n        );\n        return res;\n      } catch (error) {\n        console.error(error);\n        return {\n          currentPage: 0,\n          hasNextPage: false,\n          results: [],\n        };\n      }\n    },\n  });\n  return {\n    currentPage: results?.currentPage || 0,\n    hasNextPage: results?.hasNextPage || false,\n    results: [...fetchAnimeResultsHandler(results.results as IAnimeResult[])],\n    ...(results.totalPages && { totalPages: results.totalPages }),\n    ...(results.totalResults && { totalResults: results.totalResults }),\n  };\n};\n\nexport const getAnimeInfo = async (id: string): Promise<IAnimeInfo | undefined> => {\n  const anilist = generateAnilistMeta();\n  const results = await cachified({\n    key: `anilist-info-${id}`,\n    ttl: 1000 * 60 * 60 * 24,\n    staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n    cache: lruCache,\n    request: undefined,\n    getFreshValue: async () => {\n      try {\n        const res = (await anilist.fetchAnilistInfoById(id)) as IAnimeInfo;\n        return {\n          ...res,\n          recommendations: [...fetchAnimeResultsHandler(res.recommendations as IAnimeResult[])],\n        };\n      } catch (error) {\n        console.error(error);\n        return undefined;\n      }\n    },\n  });\n  return results;\n};\n\nexport const getAnimeEpisodeInfo = async (\n  id: string,\n  dub?: boolean,\n  provider?: string,\n  fetchFiller?: boolean,\n): Promise<IEpisodeInfo[] | undefined> => {\n  const anilist = generateAnilistMeta(provider);\n  const results = await cachified({\n    key: `anilist-episode-${id}-${dub}-${provider}-${fetchFiller}`,\n    ttl: 1000 * 60 * 60 * 4,\n    staleWhileRevalidate: 1000 * 60 * 60 * 24,\n    cache: lruCache,\n    request: undefined,\n    getFreshValue: async () => {\n      try {\n        const res = await anilist.fetchEpisodesListById(id, dub, fetchFiller);\n        return res;\n      } catch (error) {\n        console.error(error);\n        return [];\n      }\n    },\n  });\n  return results;\n};\n\nexport const getAnimeEpisodeStream = async (\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  episodeId: any,\n  provider?: string,\n  server?: string,\n) => {\n  const anilist = generateAnilistMeta(provider);\n  const results = await cachified({\n    key: `anilist-episode-stream-${episodeId}-${provider}-${server}`,\n    ttl: 1000 * 60 * 60,\n    staleWhileRevalidate: 1000 * 60 * 60 * 3,\n    cache: lruCache,\n    request: undefined,\n    getFreshValue: async () => {\n      try {\n        const res = await anilist.fetchEpisodeSources(episodeId, server);\n        return res;\n      } catch (error) {\n        console.error(error);\n        return undefined;\n      }\n    },\n  });\n  return results;\n};\n"
  },
  {
    "path": "app/services/consumet/anilist/anilist.types.ts",
    "content": "import type { IMedia } from '~/types/media';\n\nexport interface IAnimeList {\n  currentPage: number;\n  hasNextPage: boolean;\n  results: IAnimeResult[] | IAnimeEpisode[] | IMedia[];\n  totalPages?: number;\n  totalResults?: number;\n}\n\nexport interface IAnimeEpisode {\n  color?: string;\n  episodeId?: string;\n  episodeNumber?: number;\n  episodeTitle?: string;\n  genres?: string[];\n  id?: string;\n  image?: string;\n  malId?: number;\n  rating?: number;\n  title?: Title;\n  type?: Type;\n}\n\nexport interface IAnimeInfo {\n  characters?: ICharacter[];\n  color?: string;\n  countryOfOrigin?: string;\n  cover?: string;\n  description?: string;\n  duration?: number;\n  endDate?: IDate;\n  episodes: IEpisodeInfo[];\n  genres?: string[];\n  id?: string;\n  image?: string;\n  isAdult?: boolean;\n  isLicensed?: boolean;\n  malId?: number;\n  nextAiringEpisode?: IAiring;\n  popularity?: number;\n  rating?: number;\n  recommendations: IAnimeResult[] | IMedia[];\n  relations?: IAnimeResult[];\n  releaseDate?: number;\n  season?: string;\n  startDate?: IDate;\n  status?: string;\n  studios?: string[];\n  subOrDub?: 'sub' | 'dub';\n  synonyms?: string[];\n  title?: Title;\n  totalEpisodes?: number;\n  trailer?: ITrailer;\n  type?: string;\n}\n\nexport interface IAnimeEpisodeStream {\n  headers: Headers;\n  sources: Source[];\n}\n\nexport interface IAnimeResult {\n  color?: string;\n  cover?: string;\n  description?: string;\n  duration?: number;\n  episodes?: number;\n  genres?: string[];\n  id?: string;\n  image?: string;\n  malId?: number;\n  popularity?: number;\n  rating?: number;\n  relationType?: string;\n  releaseDate?: number;\n  status?: string;\n  title?: Title;\n  totalEpisodes?: number;\n  trailer?: ITrailer;\n  type?: Type;\n}\n\nexport interface Title {\n  english?: null | string;\n  native?: string;\n  romaji?: string;\n  userPreferred?: string;\n}\n\nexport interface ITrailer {\n  id?: string;\n  site?: string;\n  thumbnail?: string;\n}\n\nexport enum Type {\n  Tv = 'TV',\n  TvShort = 'TV_SHORT',\n  Ova = 'OVA',\n  Ona = 'ONA',\n  Movie = 'MOVIE',\n  Special = 'SPECIAL',\n  Music = 'MUSIC',\n}\n\nexport interface ICharacter {\n  id?: number;\n  image?: string;\n  name?: IName;\n  role?: string;\n  voiceActors?: IVoiceActor[];\n}\n\nexport interface IVoiceActor {\n  id?: number;\n  image?: string;\n  name?: IName;\n}\n\nexport interface IName {\n  first: string;\n  full: string;\n  last: null | string;\n  native: string;\n  userPreferred: string;\n}\n\nexport interface IDate {\n  day: number;\n  month: number;\n  year: number;\n}\n\nexport interface IEpisodeInfo {\n  id: string;\n  number: number;\n  title?: string;\n  description?: string;\n  isFiller?: boolean;\n  url?: string;\n  image?: string;\n  releaseDate?: string;\n  [x: string]: unknown;\n}\n\nexport interface IAiring {\n  airingTime?: number;\n  timeUntilAiring?: number;\n  episode?: number;\n}\n\nexport interface Headers {\n  Referer: string;\n}\n\nexport interface Source {\n  isM3U8?: boolean;\n  quality: string;\n  url: string;\n}\n"
  },
  {
    "path": "app/services/consumet/anilist/utils.server.ts",
    "content": "import type { IMedia } from '~/types/media';\n\nimport type { IAnimeEpisode, IAnimeResult } from './anilist.types';\n\nexport const fetchAnimeResultsHandler = (data: IAnimeResult[]): IMedia[] => {\n  const media: IMedia[] = [];\n  data.forEach((anime) => {\n    const {\n      color,\n      cover,\n      description,\n      episodes,\n      genres,\n      id,\n      image,\n      malId,\n      popularity,\n      rating,\n      relationType,\n      releaseDate,\n      status,\n      title,\n      totalEpisodes,\n      trailer,\n      type,\n    } = anime;\n    const mediaData: IMedia = {\n      backdropPath: cover,\n      color,\n      episodes,\n      genresAnime: genres,\n      id,\n      malId,\n      mediaType: 'anime',\n      overview: description,\n      popularity,\n      posterPath: image,\n      relationType,\n      releaseDate,\n      status,\n      title,\n      totalEpisodes,\n      trailer,\n      type,\n      voteAverage: rating,\n    };\n    media.push(mediaData);\n  });\n  return media;\n};\n\nexport const fetchAnimeEpisodeHandler = (data: IAnimeEpisode[]): IMedia[] => {\n  const media: IMedia[] = [];\n  data.forEach((anime) => {\n    const {\n      color,\n      episodeId,\n      episodeNumber,\n      episodeTitle,\n      genres,\n      id,\n      image,\n      malId,\n      rating,\n      title,\n      type,\n    } = anime;\n    const mediaData: IMedia = {\n      color,\n      episodeId,\n      episodeNumber,\n      episodeTitle,\n      genresAnime: genres,\n      id,\n      malId,\n      mediaType: 'anime',\n      posterPath: image,\n      title,\n      type,\n      voteAverage: rating,\n    };\n    media.push(mediaData);\n  });\n  return media;\n};\n"
  },
  {
    "path": "app/services/consumet/bilibili/bilibili.server.ts",
    "content": "import { fetcher, lruCache } from '~/utils/server/cache.server';\n\nimport type { IBilibiliEpisode, IBilibiliInfo, IBilibiliSearch } from './bilibili.types';\nimport Bilibili from './utils.server';\n\nexport const getBilibiliSearch = async (query: string): Promise<IBilibiliSearch | undefined> => {\n  try {\n    const fetched = await fetcher<IBilibiliSearch>({\n      url: Bilibili.animeSearchUrl(query),\n      key: `bilibili-search-${query}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.log(error);\n  }\n};\n\nexport const getBilibiliInfo = async (\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  id: number,\n): Promise<IBilibiliInfo | undefined> => {\n  try {\n    const fetched = await fetcher<IBilibiliInfo>({\n      url: Bilibili.animeInfoUrl(id),\n      key: `bilibili-info-${id}`,\n      ttl: 1000 * 60 * 60 * 24 * 7,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 30,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.log(error);\n  }\n};\n\nexport const getBilibiliEpisode = async (\n  episodeId: number,\n): Promise<IBilibiliEpisode | undefined> => {\n  try {\n    const fetched = await fetcher<IBilibiliEpisode>({\n      url: Bilibili.animeEpisodeUrl(episodeId),\n      key: `bilibili-episode-${episodeId}`,\n      ttl: 1000 * 60 * 60,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.log(error);\n  }\n};\n"
  },
  {
    "path": "app/services/consumet/bilibili/bilibili.types.ts",
    "content": "/* eslint-disable @typescript-eslint/no-explicit-any */\nexport interface IBilibiliSearch {\n  totalResults: number;\n  results: IBilibiliResult[];\n}\n\nexport interface IBilibiliResult {\n  id: number;\n  title: string;\n  image?: string;\n  genres?: string[];\n  rating?: number;\n  view?: string;\n}\n\nexport interface IBilibiliInfo {\n  id: string;\n  title: string;\n  description: string;\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  seasons: any[];\n  recommendations: IBilibiliResult[];\n  subOrDub: string;\n  episodes: Episode[];\n  totalEpisodes: number;\n}\n\nexport interface Episode {\n  id: string;\n  number: number;\n  title: string;\n  image: string;\n}\n\nexport interface IBilibiliEpisode {\n  success: boolean;\n  sources: Source[];\n  fonts: any[];\n  thumbnail?: any;\n  subtitles: Subtitle[];\n}\n\ninterface Subtitle {\n  file: string;\n  lang: string;\n  language: string;\n}\n\ninterface Source {\n  file: string;\n  type: string;\n  proxy: Proxy;\n}\n\ninterface Proxy {\n  redirectWithProxy: boolean;\n  followRedirect: boolean;\n  appendReqHeaders: any;\n}\n"
  },
  {
    "path": "app/services/consumet/bilibili/utils.server.ts",
    "content": "import { env } from 'process';\n\nexport default class Bilibili {\n  static readonly API_BASE_URL = env.BILIBILI_API_URL;\n\n  static readonly API_EPISODE_URL = env.BILIBILI_API_EPISODE_URL;\n\n  static animeSearchUrl = (query: string): string => `${Bilibili.API_BASE_URL}${query}`;\n\n  static animeInfoUrl = (id: number): string => `${Bilibili.API_BASE_URL}info?id=${id}`;\n\n  static animeEpisodeUrl = (episodeId: number): string =>\n    `${Bilibili.API_EPISODE_URL}?episode_id=${episodeId}&source_media_id=1&source_id=bilibili`;\n}\n"
  },
  {
    "path": "app/services/i18n/i18n.config.ts",
    "content": "export const DEFAULT_LANGUAGE = 'en';\n\nexport const languages = [\n  { code: 'en', name: 'English' },\n  { code: 'fr', name: 'Français' },\n  { code: 'vi', name: 'Tiếng Việt' },\n] as const;\n\nexport const i18n = {\n  // This is the list of languages your application supports\n  supportedLngs: languages.map((l) => l.code),\n  // This is the language you want to use in case\n  // if the user language is not in the supportedLngs\n  fallbackLng: DEFAULT_LANGUAGE,\n  // The default namespace of i18next is \"translation\", but you can customize it here\n  defaultNS: 'common',\n  // Disabling suspense is recommended\n  react: { useSuspense: false },\n  interpolation: {\n    escapeValue: false,\n  },\n};\n"
  },
  {
    "path": "app/services/i18n/i18next.server.ts",
    "content": "import { resolve } from 'node:path';\nimport { createCookie } from '@remix-run/node';\nimport Backend from 'i18next-fs-backend';\nimport { RemixI18Next } from 'remix-i18next';\n\nimport { i18n } from './i18n.config';\n\nconst TEN_YEARS_IN_SECONDS = 10 * 365 * 24 * 60 * 60;\n\nexport const i18nCookie = createCookie('i18n', {\n  sameSite: 'lax',\n  path: '/',\n  maxAge: TEN_YEARS_IN_SECONDS,\n});\n\nexport const i18next = new RemixI18Next({\n  detection: {\n    cookie: i18nCookie,\n    supportedLanguages: i18n.supportedLngs,\n    fallbackLanguage: i18n.fallbackLng,\n  },\n  // This is the configuration for i18next used\n  // when translating messages server-side only\n  i18next: {\n    ...i18n,\n    backend: {\n      loadPath: resolve('./public/locales/{{lng}}/{{ns}}.json'),\n    },\n  },\n  backend: Backend,\n});\n"
  },
  {
    "path": "app/services/i18n/index.ts",
    "content": "export * from './i18n.config';\nexport * from './i18next.server';\n"
  },
  {
    "path": "app/services/kisskh/kisskh.server.ts",
    "content": "import { fetcher, lruCache } from '~/utils/server/cache.server';\n\nimport type { IEpisodeVideo, IItemInfo, ISearchItem, IVideoSubtitle } from './kisskh.types';\nimport KissKh from './utils.server';\n\nexport const getKissKhSearch = async (\n  query: string,\n  type?: number,\n): Promise<ISearchItem[] | undefined> => {\n  try {\n    const fetched = await fetcher<ISearchItem[]>({\n      url: KissKh.searchUrl(query, type),\n      key: `kisskh-search-${query}-${type}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.log(error);\n  }\n};\n\nexport const getKissKhInfo = async (id: number): Promise<IItemInfo | undefined> => {\n  try {\n    const fetched = await fetcher<IItemInfo>({\n      url: KissKh.infoUrl(id),\n      key: `kisskh-info-${id}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.log(error);\n  }\n};\n\nexport const getKissKhEpisodeStream = async (\n  episodeId: number,\n): Promise<IEpisodeVideo | undefined> => {\n  try {\n    const fetched = await fetcher<IEpisodeVideo>({\n      url: KissKh.episodeUrl(episodeId),\n      key: `kisskh-episode-${episodeId}`,\n      ttl: 1000 * 60 * 60,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.log(error);\n  }\n};\n\nexport const getKissKhEpisodeSubtitle = async (\n  episodeId: number,\n): Promise<IVideoSubtitle[] | undefined> => {\n  try {\n    const fetched = await fetcher<IVideoSubtitle[]>({\n      url: KissKh.subUrl(episodeId),\n      key: `kisskh-subtitle-${episodeId}`,\n      ttl: 1000 * 60 * 60,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.log(error);\n  }\n};\n"
  },
  {
    "path": "app/services/kisskh/kisskh.types.ts",
    "content": "/* eslint-disable @typescript-eslint/no-explicit-any */\nexport interface ISearchItem {\n  episodesCount: number;\n  label: string;\n  favoriteID: number;\n  thumbnail: string;\n  id: number;\n  title: string;\n}\n\nexport interface IItemInfo {\n  description: string;\n  releaseDate: string;\n  trailer: string;\n  country: string;\n  status: string;\n  type: string;\n  nextEpDateID: number;\n  episodes: Episode[];\n  episodesCount: number;\n  label?: any;\n  favoriteID: number;\n  thumbnail: string;\n  id: number;\n  title: string;\n}\n\ninterface Episode {\n  id: number;\n  number: number;\n  sub: number;\n}\n\nexport interface IEpisodeVideo {\n  Video: string;\n  Video_tmp: string;\n  ThirdParty: string;\n  Type: number;\n  id?: any;\n  dataSaver?: any;\n  a?: any;\n  b?: any;\n  dType?: any;\n}\n\nexport interface IVideoSubtitle {\n  src: string;\n  label: string;\n  land: string;\n  default: boolean;\n}\n"
  },
  {
    "path": "app/services/kisskh/utils.server.ts",
    "content": "import { env } from 'process';\n\nexport default class KissKh {\n  static readonly API_BASE_URL = env.KISSKH_API_URL;\n\n  /**\n   * It fetches a list of movies from the TMDB API, and returns a list of movies\n   * @param query The query to search for\n   * @param type The type of media to search for (0: All, 1: TV Series, 2: Movie, 3: Anime, 4: Hollywood)\n   * @returns string\n   * query: string\n   * type: number | undefined\n   */\n  static searchUrl = (query: string, type?: number): string =>\n    `${KissKh.API_BASE_URL}DramaList/Search?q=${query}&type=${type || 0}`;\n\n  static infoUrl = (id: number): string => `${KissKh.API_BASE_URL}DramaList/Drama/${id}?isq=false`;\n\n  static episodeUrl = (episodeId: number): string =>\n    `${KissKh.API_BASE_URL}DramaList/Episode/${episodeId}.png?err=false&ts=&time=`;\n\n  static subUrl = (episodeId: number): string => `${KissKh.API_BASE_URL}Sub/${episodeId}`;\n}\n"
  },
  {
    "path": "app/services/loklok/index.ts",
    "content": "export * from './movie.server';\nexport * from './tv.server';\nexport * from './media.server';\n"
  },
  {
    "path": "app/services/loklok/loklok.type.ts",
    "content": "import type { IMovieSource } from '~/services/tmdb/tmdb.types';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\nexport interface ILoklokSubtitle {\n  language: string;\n  url: string;\n  lang: string;\n}\n\nexport interface ILoklokSources {\n  isM3U8?: boolean;\n  quality: number;\n  url: string;\n}\n\nexport interface ILoklokTag {\n  id: number;\n  name: string;\n}\n\nexport interface ILoklokLikeItem {\n  areaList: {\n    id: 37;\n    name: 'France';\n  }[];\n  areaNameList: string[];\n  category: number;\n  coverHorizontalUrl: string;\n  coverVerticalUrl: string;\n  drameTypeVo: any;\n  id: string;\n  name: string;\n  score: number;\n  tagList: ILoklokTag[];\n  tagNameList: string[];\n  upImgUrl: string;\n  upName: string;\n  year: number;\n}\n\nexport interface ILoklokRefItem {\n  category: 0 | 1;\n  coverHorizontalUrl: string;\n  coverVerticalUrl: string;\n  drameTypeVo: any;\n  id: string;\n  name: string;\n  seriesNo: number;\n}\n\nexport interface ILoklokStarItem {\n  image: string;\n  localName: string;\n  role: string;\n  roleName: string;\n  starId: number | string;\n}\nexport interface ILoklokInfoData {\n  aliasName: string;\n  areaList: {\n    id: number;\n    name: string;\n  }[];\n  areaNameList: string[];\n  category: 0 | 1;\n  coverHorizontalUrl: string;\n  coverHorizontalUrlJson: string;\n  coverVerticalUrl: string;\n  drameTypeVo: {\n    drameName: string;\n    drameType: string;\n  };\n  episodeCount: number | null;\n  episodeRoomListVo: {\n    category: 0 | 1;\n    episodeId: string;\n    episodeName: string;\n    number: number;\n    roomId: string;\n    seasonID: string;\n    seasonName: string;\n  };\n  episodeVo: any[];\n  id: string;\n  introduction: string;\n  likeList: ILoklokLikeItem[];\n  name: string;\n  nameJson: string;\n  refList: ILoklokRefItem[];\n  reserved: boolean;\n  score: number;\n  seriesNo: number;\n  showSetName: boolean;\n  starList: ILoklokStarItem[];\n  tagList: ILoklokTag[];\n  tagListName: string[];\n  translateType: number;\n  upInfo: {\n    upId: number;\n    upImgUrl: string;\n    upName: string;\n  };\n  updateInfo: any;\n  year: number;\n}\n\nexport interface ILoklokMediaInfo {\n  data: ILoklokInfoData;\n  sources: IMovieSource[];\n  subtitles: ILoklokSubtitle[];\n}\n\nexport interface ILoklokSearchData {\n  areas: {\n    id: number;\n    name: string;\n  }[];\n  categoryTag: ILoklokTag[];\n  coverHorizontalUrl: string;\n  coverVerticalUrl: string;\n  domainType: number;\n  dramaType: {\n    code: string;\n    name: string;\n  };\n  duration: string;\n  id: string;\n  name: string;\n  releaseTime: string;\n  sort: string;\n  upInfo: {\n    enable: boolean;\n    upId: number;\n    upImgUrl: string;\n    upName: string;\n    userId: any;\n  };\n}\n"
  },
  {
    "path": "app/services/loklok/media.server.ts",
    "content": "import { fetcher, lruCache } from '~/utils/server/cache.server';\n\nimport { LOKLOK_URL } from './utils.server';\n\nexport const loklokGetMedia = async (contentId: string, episodeIndex: string, category: 0 | 1) => {\n  try {\n    const res = await fetcher({\n      url: `${LOKLOK_URL}/api/media?contentId=${contentId}&episodeIndex=${episodeIndex}&category=${category}`,\n      key: `loklok-media-${contentId}-${episodeIndex}-${category}`,\n      cache: lruCache,\n      ttl: 1000 * 60 * 60,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24,\n    });\n\n    return res;\n  } catch (e) {\n    console.error(e);\n  }\n};\n"
  },
  {
    "path": "app/services/loklok/movie.server.ts",
    "content": "import { fetcher, lruCache } from '~/utils/server/cache.server';\n\nimport type { ILoklokMediaInfo, ILoklokSearchData } from './loklok.type';\nimport { LOKLOK_URL } from './utils.server';\n\n/**\n * Get movie info by id\n * @param id loklok id\n * @returns object { data: some movie info, sources: media sources, subtitles}\n */\nexport const loklokGetMovieInfo = async (id: string) => {\n  try {\n    const info = await fetcher<ILoklokMediaInfo>({\n      url: `${LOKLOK_URL}/movie/detail?id=${id}`,\n      key: `loklok-movie-info-${id}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n\n    if (info && info.data) return info;\n  } catch (e) {\n    console.error(e);\n  }\n};\n\n/**\n * Search movie info by name\n * @param title movie's title\n * @param orgTitle movie's another title\n * @param year release year\n * @returns array [{ movie info }]\n */\nexport const loklokSearchMovie = async (title: string, orgTitle: string, year: number) => {\n  try {\n    const res = await fetcher<{ data: ILoklokSearchData }>({\n      url: `${LOKLOK_URL}/search/one?title=${title}&orgTitle=${orgTitle}&year=${year}&season=`,\n      key: `loklok-search-one-movie-${title}-${orgTitle}-${year}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n\n    if (res && res.data) return res;\n  } catch (e) {\n    console.error(e);\n  }\n};\n\n/**\n * Search movie info by name\n * @param title movie's title\n * @param orgTitle movie's another title\n * @param year release year\n * @returns object { data: some movie info, sources: media sources, subtitles}\n */\nexport const loklokSearchMovieInfo = async (title: string, orgTitle: string, year: number) => {\n  try {\n    const res = await fetcher<{ data: ILoklokSearchData }>({\n      url: `${LOKLOK_URL}/search/one?title=${title}&orgTitle=${orgTitle}&year=${year}&season=`,\n      key: `loklok-search-one-movie-${title}-${orgTitle}-${year}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n\n    if (!res || !res.data) return;\n\n    const info = await fetcher<ILoklokMediaInfo>({\n      url: `${LOKLOK_URL}/movie/detail?id=${res.data.id}`,\n      key: `loklok-movie-info-${res.data.id}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n\n    if (info && info.data) return info;\n  } catch (e) {\n    console.error(e);\n  }\n};\n\n/**\n * Get movie subtitles by id\n * @param id movie's title\n * @returns array of { url: string, lang: string }\n */\nexport const loklokGetMovieSub = async (id: string) => {\n  try {\n    const info = await fetcher<ILoklokMediaInfo>({\n      url: `${LOKLOK_URL}/movie/detail?id=${id}`,\n      key: `loklok-movie-info-${id}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n\n    if (info && info.data) {\n      return info.subtitles.map((sub) => ({\n        lang: `${sub.language} (${sub.lang})`,\n        url: `${LOKLOK_URL}/subtitle?url=${sub.url}`,\n      }));\n    }\n\n    return [];\n  } catch (e) {\n    console.error(e);\n    return [];\n  }\n};\n\n/**\n * Search movie subtitles\n * @param title movie's title\n * @param orgTitle movie's another title\n * @param year release year\n * @returns array of { url: string, lang: string}\n */\nexport const loklokSearchMovieSub = async (title: string, orgTitle: string, year: number) => {\n  try {\n    const res = await fetcher<{ data: ILoklokSearchData }>({\n      url: `${LOKLOK_URL}/search/one?title=${title}&orgTitle=${orgTitle}&year=${year}&season=`,\n      key: `loklok-search-one-movie-${title}-${orgTitle}-${year}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n\n    if (!res || !res.data) return [];\n\n    const info = await fetcher<ILoklokMediaInfo>({\n      url: `${LOKLOK_URL}/movie/detail?id=${res.data.id}`,\n      key: `loklok-movie-info-${res.data.id}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n\n    if (info && info.data) {\n      return info.subtitles.map((sub) => ({\n        lang: `${sub.language} (${sub.lang})`,\n        url: `${LOKLOK_URL}/subtitle?url=${sub.url}`,\n      }));\n    }\n\n    return [];\n  } catch (e) {\n    console.error(e);\n    return [];\n  }\n};\n"
  },
  {
    "path": "app/services/loklok/tv.server.ts",
    "content": "import { fetcher, lruCache } from '~/utils/server/cache.server';\n\nimport type { ILoklokInfoData, ILoklokMediaInfo, ILoklokSearchData } from './loklok.type';\nimport { LOKLOK_URL } from './utils.server';\n\n/**\n * It takes a string as an argument, and returns a promise that resolves to an array of objects\n * @param {string} title - The title of the TV show you want to search for.\n * @returns An array of objects.\n */\nexport const loklokSearchOneTv = async (\n  title: string,\n  orgTitle: string,\n  year: number,\n  season?: number,\n) => {\n  try {\n    let url = `${LOKLOK_URL}/search/one?title=${title}&orgTitle=${orgTitle}&year=${year}`;\n    if (season) url += `&season=${season}`;\n    const res = await fetcher<{ data: ILoklokSearchData }>({\n      url,\n      key: `loklok-search-one-tv-${title}-${orgTitle}-${year}-${season}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n    if (res) return res;\n    return undefined;\n  } catch (e) {\n    console.error(e);\n  }\n};\n\n/**\n * Get tv/anime episode info by id\n * @param id loklok id\n * @param episodeIndex 0, 1, 2, 3, ...\n * @returns object { data: some movie info, sources: media sources, subtitles}\n */\nexport const loklokGetTvEpInfo = async (id: string, episodeIndex = 0) => {\n  try {\n    const info = await fetcher<ILoklokMediaInfo>({\n      url: `${LOKLOK_URL}/tv/detail?id=${id}&episodeIndex=${episodeIndex}`,\n      key: `loklok-tv-ep-info-${id}-${episodeIndex}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n\n    if (info && info.data) return info;\n    return undefined;\n  } catch (e) {\n    console.error(e);\n  }\n};\n\nexport const getLoklokOrgDetail = async (\n  id: string,\n  type: string,\n): Promise<ILoklokInfoData | undefined> => {\n  try {\n    const info = await fetcher<ILoklokMediaInfo>({\n      url: `${LOKLOK_URL}/${type}/orgDetail?id=${id}`,\n      key: `loklok-org-detail-${id}-${type}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n\n    if (info && info.data) return info.data;\n    return undefined;\n  } catch (e) {\n    console.error(e);\n  }\n};\n\n/**\n * Search series by title, release year, bla...\n * @param title series's title\n * @param orgTitle another title\n * @param year release year\n * @param season 1, 2, 3, ...\n * @param episodeIndex 0, 1, 2, 3,...\n * @returns object { data: some movie info, sources: media sources, subtitles}\n */\nexport const loklokSearchTvEpInfo = async (\n  title: string,\n  orgTitle: string,\n  year: number,\n  season = 1,\n  episodeIndex = 0,\n) => {\n  try {\n    const res = await fetcher<{ data: ILoklokSearchData }>({\n      url: `${LOKLOK_URL}/search/one?title=${title}&orgTitle=${orgTitle}&year=${year}&season=${season}`,\n      key: `loklok-search-one-tv-${title}-${orgTitle}-${year}-${season}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n\n    if (!res || !res.data) return;\n\n    const info = await fetcher<ILoklokMediaInfo>({\n      url: `${LOKLOK_URL}/tv/detail?id=${res.data.id}&episodeIndex=${episodeIndex}`,\n      key: `loklok-tv-ep-info-${res.data.id}-${episodeIndex}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n\n    if (info && info.data) return info;\n  } catch (e) {\n    console.error(e);\n  }\n};\n\n/**\n *\n * @param id loklok id\n * @param episodeIndex 0, 1, 2, 3,...\n * @returns\n */\nexport const loklokGetTvEpSub = async (id: string, episodeIndex = 0) => {\n  try {\n    const info = await fetcher<ILoklokMediaInfo>({\n      url: `${LOKLOK_URL}/tv/detail?id=${id}&episodeIndex=${episodeIndex}`,\n      key: `loklok-tv-ep-info-${id}-${episodeIndex}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n\n    if (info && info.data) {\n      return info.subtitles.map((sub) => ({\n        lang: `${sub.language} (${sub.lang})`,\n        url: `${LOKLOK_URL}/subtitle?url=${sub.url}`,\n      }));\n    }\n    return [];\n  } catch (e) {\n    console.error(e);\n    return [];\n  }\n};\n\n/**\n * Search series episode subtitles\n * @param title series title\n * @param orgTitle another title\n * @param year release year\n * @param season 1, 2, 3, ...\n * @param episodeIndex 0, 1, 2, ...\n * @returns array of { url: string, lang: string }\n */\nexport const loklokSearchTvEpSub = async (\n  title: string,\n  orgTitle: string,\n  year: number,\n  season = 1,\n  episodeIndex = 0,\n) => {\n  try {\n    const res = await fetcher<{ data: ILoklokSearchData }>({\n      url: `${LOKLOK_URL}/search/one?title=${title}&orgTitle=${orgTitle}&year=${year}&season=${season}`,\n      key: `loklok-search-one-tv-${title}-${orgTitle}-${year}-${season}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n\n    if (!res || !res.data) return [];\n\n    const info = await fetcher<ILoklokMediaInfo>({\n      url: `${LOKLOK_URL}/tv/detail?id=${res.data.id}&episodeIndex=${episodeIndex}`,\n      key: `loklok-tv-ep-info-${res.data.id}-${episodeIndex}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n\n    if (info && info.data) {\n      return info.subtitles.map((sub) => ({\n        lang: `${sub.language} (${sub.lang})`,\n        url: `${LOKLOK_URL}/subtitle?url=${sub.url}`,\n      }));\n    }\n\n    return [];\n  } catch (e) {\n    console.error(e);\n    return [];\n  }\n};\n"
  },
  {
    "path": "app/services/loklok/utils.server.ts",
    "content": "import { env } from 'process';\n\n// eslint-disable-next-line import/prefer-default-export\nexport const LOKLOK_URL = env.LOKLOK_API_URL;\n"
  },
  {
    "path": "app/services/open-subtitles/open-subtitles.server.ts",
    "content": "import type * as C from 'cachified';\n\nimport { cachified, lruCache } from '~/utils/server/cache.server';\n\nimport type { ISubtitleDownload, ISubtitlesSearch } from './open-subtitles.types';\nimport Opensubtitles from './utils.server';\n\nconst fetcher = async <Value>({\n  url,\n  method,\n  body,\n  ...options\n}: Omit<C.CachifiedOptions<Value>, 'getFreshValue' | 'forceFresh'> & {\n  url: string;\n  method: string;\n  body?: { file_id: number; sub_format: string };\n  forceFresh?: boolean | string;\n  getFreshValue?: undefined;\n}): Promise<Value> => {\n  const results = await cachified({\n    ...options,\n    request: undefined,\n    getFreshValue: async () => {\n      const myHeaders = new Headers();\n      myHeaders.append('Api-Key', `${process.env.OPEN_SUBTITLES_API_KEY}`);\n      myHeaders.append('Content-Type', 'application/json');\n      const init = {\n        method,\n        headers: myHeaders,\n        ...(body && { body: JSON.stringify(body) }),\n      };\n      const res = await fetch(url, init);\n      if (!res.ok) throw new Error(JSON.stringify(await res.json()));\n      const data = (await res.json()) as Value;\n      return data;\n    },\n  });\n  return results;\n};\n\nexport const getSubtitlesSearch = async (\n  id?: number,\n  imdb_id?: number,\n  tmdb_id?: number,\n  parent_feature_id?: number,\n  parent_imdb_id?: number,\n  parent_tmdb_id?: number,\n  query?: string,\n  ai_translated?: 'exclude' | 'include',\n  episode_number?: number,\n  foreign_parts_only?: 'exclude' | 'include' | 'only',\n  hearing_impaired?: 'exclude' | 'include' | 'only',\n  languages?: string,\n  machine_translated?: 'exclude' | 'include',\n  moviehash?: string,\n  moviehash_match?: string,\n  order_by?: string,\n  order_direction?: 'asc' | 'desc',\n  page?: number,\n  season_number?: number,\n  trusted_sources?: 'include' | 'only',\n  type?: 'movie' | 'episode' | 'all',\n  user_id?: number,\n  year?: number,\n): Promise<ISubtitlesSearch | undefined> => {\n  try {\n    const fetched = await fetcher<ISubtitlesSearch>({\n      url: Opensubtitles.subtitlesSearchUrl(\n        id,\n        imdb_id,\n        tmdb_id,\n        parent_feature_id,\n        parent_imdb_id,\n        parent_tmdb_id,\n        query,\n        ai_translated,\n        episode_number,\n        foreign_parts_only,\n        hearing_impaired,\n        languages,\n        machine_translated,\n        moviehash,\n        moviehash_match,\n        order_by,\n        order_direction,\n        page,\n        season_number,\n        trusted_sources,\n        type,\n        user_id,\n        year,\n      ),\n      method: 'GET',\n      key: `subtitles-search-${id}-${imdb_id}-${tmdb_id}-${parent_feature_id}-${parent_imdb_id}-${parent_tmdb_id}-${query}-${ai_translated}-${episode_number}-${foreign_parts_only}-${hearing_impaired}-${languages}-${machine_translated}-${moviehash}-${moviehash_match}-${order_by}-${order_direction}-${page}-${season_number}-${trusted_sources}-${type}-${user_id}-${year}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.error(error);\n  }\n};\n\nexport const getSubtitleDownload = async (\n  id: number,\n  sub_format: 'srt' | 'webvtt',\n): Promise<ISubtitleDownload | undefined> => {\n  try {\n    const fetched = await fetcher<ISubtitleDownload>({\n      url: Opensubtitles.subtitleDownloadUrl(),\n      method: 'POST',\n      body: {\n        file_id: id,\n        sub_format,\n      },\n      key: `subtitle-download-${id}-${sub_format}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.error(error);\n  }\n};\n"
  },
  {
    "path": "app/services/open-subtitles/open-subtitles.types.ts",
    "content": "export interface ISubtitlesSearch {\n  data: ISubtitle[];\n  page: number;\n  per_page: number;\n  total_count: number;\n  total_pages: number;\n}\n\nexport interface ISubtitleDownload {\n  file_name: string;\n  link: string;\n  message: string;\n  remaining: number;\n  requests: number;\n  reset_time: string;\n  reset_time_utc: Date;\n}\n\nexport interface ISubtitle {\n  attributes: IAttributes;\n  id: string;\n  type: string;\n}\n\nexport interface IAttributes {\n  ai_translated: boolean;\n  comments: string;\n  download_count: number;\n  feature_details: IFeatureDetails;\n  files: IFile[];\n  foreign_parts_only: boolean;\n  fps: number;\n  from_trusted: boolean | null;\n  hd: boolean;\n  hearing_impaired: boolean;\n  language: string;\n  legacy_subtitle_id: number | null;\n  machine_translated: boolean;\n  new_download_count: number;\n  ratings: number;\n  related_links: IRelatedLinks;\n  release: string;\n  subtitle_id: string;\n  upload_date: Date;\n  uploader: IUploader;\n  url: string;\n  votes: number;\n}\n\nexport interface IFeatureDetails {\n  feature_id: number;\n  feature_type: string;\n  imdb_id: number;\n  movie_name: string;\n  title: string;\n  tmdb_id: number;\n  year: number;\n}\n\nexport interface IFile {\n  cd_number: number;\n  file_id: number;\n  file_name: null | string;\n}\n\nexport interface IRelatedLinks {\n  img_url: string;\n  label: string;\n  url: string;\n}\n\nexport interface IUploader {\n  name: string;\n  rank: string;\n  uploader_id: number | null;\n}\n"
  },
  {
    "path": "app/services/open-subtitles/utils.server.ts",
    "content": "export default class Opensubtitles {\n  static readonly API_BASE_URL = 'https://api.opensubtitles.com/api/v1/';\n\n  static subtitlesSearchUrl = (\n    id?: number,\n    imdb_id?: number,\n    tmdb_id?: number,\n    parent_feature_id?: number,\n    parent_imdb_id?: number,\n    parent_tmdb_id?: number,\n    query?: string,\n    ai_translated?: 'exclude' | 'include',\n    episode_number?: number,\n    foreign_parts_only?: 'exclude' | 'include' | 'only',\n    hearing_impaired?: 'exclude' | 'include' | 'only',\n    languages?: string,\n    machine_translated?: 'exclude' | 'include',\n    moviehash?: string,\n    moviehash_match?: string,\n    order_by?: string,\n    order_direction?: 'asc' | 'desc',\n    page?: number,\n    season_number?: number,\n    trusted_sources?: 'include' | 'only',\n    type?: 'movie' | 'episode' | 'all',\n    user_id?: number,\n    year?: number,\n  ): string => {\n    let url = `${Opensubtitles.API_BASE_URL}subtitles`;\n    const params = new URLSearchParams();\n    if (id) {\n      params.append('id', id.toString());\n    }\n    if (imdb_id) {\n      params.append('imdb_id', imdb_id.toString());\n    }\n    if (tmdb_id) {\n      params.append('tmdb_id', tmdb_id.toString());\n    }\n    if (parent_feature_id) {\n      params.append('parent_feature_id', parent_feature_id.toString());\n    }\n    if (parent_imdb_id) {\n      params.append('parent_imdb_id', parent_imdb_id.toString());\n    }\n    if (parent_tmdb_id) {\n      params.append('parent_tmdb_id', parent_tmdb_id.toString());\n    }\n    if (query) {\n      params.append('query', query);\n    }\n    if (ai_translated) {\n      params.append('ai_translated', ai_translated);\n    }\n    if (episode_number) {\n      params.append('episode_number', episode_number.toString());\n    }\n    if (foreign_parts_only) {\n      params.append('foreign_parts_only', foreign_parts_only);\n    }\n    if (hearing_impaired) {\n      params.append('hearing_impaired', hearing_impaired);\n    }\n    if (languages) {\n      params.append('languages', languages);\n    }\n    if (machine_translated) {\n      params.append('machine_translated', machine_translated);\n    }\n    if (moviehash) {\n      params.append('moviehash', moviehash);\n    }\n    if (moviehash_match) {\n      params.append('moviehash_match', moviehash_match);\n    }\n    if (order_by) {\n      params.append('order_by', order_by);\n    }\n    if (order_direction) {\n      params.append('order_direction', order_direction);\n    }\n    if (page) {\n      params.append('page', page.toString());\n    }\n    if (season_number) {\n      params.append('season_number', season_number.toString());\n    }\n    if (trusted_sources) {\n      params.append('trusted_sources', trusted_sources);\n    }\n    if (type) {\n      params.append('type', type);\n    }\n    if (user_id) {\n      params.append('user_id', user_id.toString());\n    }\n    if (year) {\n      params.append('year', year.toString());\n    }\n    if (params.toString()) {\n      url += `?${params.toString()}`;\n    }\n    return url;\n  };\n\n  static subtitleDownloadUrl = (): string => `${Opensubtitles.API_BASE_URL}download`;\n}\n"
  },
  {
    "path": "app/services/provider.server.ts",
    "content": "import { type IMovieInfo } from '@consumet/extensions';\n\nimport sgConfigs from '~/services/configs.server';\nimport { getAnimeEpisodeInfo } from '~/services/consumet/anilist/anilist.server';\nimport { getBilibiliInfo, getBilibiliSearch } from '~/services/consumet/bilibili/bilibili.server';\nimport type { IBilibiliResult } from '~/services/consumet/bilibili/bilibili.types';\nimport { getKissKhInfo, getKissKhSearch } from '~/services/kisskh/kisskh.server';\nimport type { ISearchItem } from '~/services/kisskh/kisskh.types';\nimport { getLoklokOrgDetail, loklokSearchMovie, loklokSearchOneTv } from '~/services/loklok';\nimport { getInfoWithProvider } from '~/services/tmdb/tmdb.server';\nimport { cachified, lruCache } from '~/utils/server/cache.server';\n\nexport type Provider = {\n  id?: string | number | null;\n  provider: string;\n  episodesCount?: number;\n};\n\nconst getProviderList = async ({\n  type,\n  title,\n  orgTitle,\n  year,\n  season,\n  animeId,\n  animeType,\n  isEnded,\n  tmdbId,\n}: {\n  /**\n   * @param {string} type - movie, tv, anime\n   */\n  type: string;\n  /**\n   * @param {string} title - title of movie, tv, anime\n   */\n  title: string;\n  /**\n   * @param {string} orgTitle - original title of movie, tv, anime\n   */\n  orgTitle?: string | null;\n  /**\n   * @param {number} year - year of movie, tv, anime\n   */\n  year?: number | string | null;\n  /**\n   * @param {number} season - season of tv\n   */\n  season?: number | string | null;\n  /**\n   * @param {number} animeId - id of anime\n   */\n  animeId?: number;\n  /**\n   * @param {string} animeType - type of anime\n   * @example 'tv', 'movie'\n   */\n  animeType?: string | null;\n  /**\n   * @param {boolean} isEnded - is movie, tv, anime ended or canceled\n   */\n  isEnded?: boolean;\n  /**\n   * @param {number} tmdbId - tmdb id of movie, tv\n   * @description if tmdbId is provided, it will be used to get provider list\n   * @example 123456\n   * @default undefined\n   */\n  tmdbId?: number;\n}): Promise<Provider[] | undefined> => {\n  const getProviders = async () => {\n    if (type === 'movie') {\n      const [infoWithProvider, loklokSearch, kisskhSearch] = await Promise.all([\n        tmdbId ? getInfoWithProvider(tmdbId.toString(), 'movie') : undefined,\n        sgConfigs.__loklokProvider\n          ? loklokSearchMovie(title, orgTitle || '', Number(year))\n          : undefined,\n        sgConfigs.__kisskhProvider ? getKissKhSearch(title) : undefined,\n      ]);\n      const provider: Provider[] = (infoWithProvider as IMovieInfo)?.episodeId\n        ? [\n            {\n              id: (infoWithProvider as IMovieInfo).id,\n              provider: 'Flixhq',\n            },\n          ]\n        : [];\n      const findKissKh: ISearchItem | undefined = kisskhSearch?.find((item) =>\n        item.title.includes(' (')\n          ? item.title.replace(/ *\\([^)]*\\) */g, '').toLowerCase() === title.toLowerCase()\n          : item.title.toLowerCase() === title.toLowerCase(),\n      );\n      if (findKissKh && findKissKh.id)\n        provider.push({\n          id: findKissKh.id,\n          provider: 'KissKh',\n        });\n      if (loklokSearch?.data.name.toLowerCase() === title.toLowerCase())\n        provider.push({\n          id: loklokSearch.data.id,\n          provider: 'Loklok',\n        });\n      return provider;\n    }\n    if (type === 'tv') {\n      const [infoWithProvider, loklokSearch, kisskhSearch] = await Promise.all([\n        tmdbId ? getInfoWithProvider(tmdbId.toString(), 'tv') : undefined,\n        sgConfigs.__loklokProvider\n          ? loklokSearchOneTv(\n              `${title} ${Number(season) > 1 ? `Season ${season}` : ''}`,\n              orgTitle || '',\n              Number(year),\n              Number(season),\n            )\n          : undefined,\n        sgConfigs.__kisskhProvider ? getKissKhSearch(title) : undefined,\n      ]);\n      const findTvSeason = (infoWithProvider as IMovieInfo)?.seasons?.find(\n        (s) => s.season === Number(season),\n      );\n      const provider: Provider[] = findTvSeason?.episodes.some((e) => e.id)\n        ? [\n            {\n              id: (infoWithProvider as IMovieInfo).id,\n              provider: 'Flixhq',\n              episodesCount: findTvSeason?.episodes.filter((e) => e.id).length,\n            },\n          ]\n        : [];\n      const findKissKh: ISearchItem | undefined = kisskhSearch?.find((item) => {\n        if (item && item.title && item.title.includes('Season')) {\n          const [itemTitle, seasonNumber] = item.title.split(' - Season ');\n          return (\n            itemTitle.toLowerCase() === title.toLowerCase() &&\n            Number(seasonNumber) === Number(season)\n          );\n        }\n        return item?.title.toLowerCase() === title.toLowerCase();\n      });\n      const [loklokDetail, kissKhDetail] = await Promise.all([\n        loklokSearch && loklokSearch.data?.id\n          ? getLoklokOrgDetail(loklokSearch.data.id, 'tv')\n          : undefined,\n        findKissKh && findKissKh.id ? getKissKhInfo(findKissKh.id) : undefined,\n      ]);\n      if (findKissKh && findKissKh.id)\n        provider.push({\n          id: findKissKh.id,\n          provider: 'KissKh',\n          episodesCount: kissKhDetail?.episodesCount || 0,\n        });\n      if (loklokSearch && loklokSearch?.data?.id)\n        provider.push({\n          id: loklokSearch?.data?.id,\n          provider: 'Loklok',\n          episodesCount: loklokDetail?.episodeVo ? loklokDetail.episodeVo.length : 0,\n        });\n      return provider;\n    }\n    if (type === 'anime') {\n      const [bilibiliSearch, loklokSearch, kisskhSearch, gogoEpisodes, zoroEpisodes] =\n        await Promise.all([\n          sgConfigs.__bilibiliProvider ? getBilibiliSearch(title) : undefined,\n          sgConfigs.__loklokProvider\n            ? loklokSearchOneTv(title, orgTitle || '', Number(year))\n            : undefined,\n          sgConfigs.__kisskhProvider ? getKissKhSearch(title, 3) : undefined,\n          getAnimeEpisodeInfo(animeId?.toString() || '', undefined, 'gogoanime'),\n          getAnimeEpisodeInfo(animeId?.toString() || '', undefined, 'zoro'),\n        ]);\n      const provider: Provider[] = [];\n      if (gogoEpisodes && gogoEpisodes.length > 0) {\n        provider.push({\n          id: animeId,\n          provider: 'Gogo',\n          episodesCount: gogoEpisodes.length,\n        });\n      }\n      if (zoroEpisodes && zoroEpisodes.length > 0) {\n        provider.push({\n          id: animeId,\n          provider: 'Zoro',\n          episodesCount: zoroEpisodes.length,\n        });\n      }\n      const findKissKh: ISearchItem | undefined = kisskhSearch?.find((item) =>\n        item.title.includes(' - ') ? item.title.split(' - ')[1] === title : item.title === title,\n      );\n      const findBilibili: IBilibiliResult | undefined = bilibiliSearch?.results.find((anime) => {\n        if (anime.title.includes('×')) {\n          return (\n            anime.title.replace(/×/g, 'x').toLowerCase() === title.replace(/\\s/g, '').toLowerCase()\n          );\n        }\n        return anime.title.toLowerCase() === title.toLowerCase();\n      });\n      const [loklokDetail, kissKhDetail, bilibiliDetail] = await Promise.all([\n        loklokSearch && loklokSearch.data?.id\n          ? getLoklokOrgDetail(loklokSearch.data.id, animeType || 'tv')\n          : undefined,\n        findKissKh && findKissKh.id ? getKissKhInfo(findKissKh.id) : undefined,\n        findBilibili && findBilibili.id ? getBilibiliInfo(findBilibili.id) : undefined,\n      ]);\n      if (findKissKh && findKissKh.id)\n        provider.push({\n          id: findKissKh.id.toString(),\n          provider: 'KissKh',\n          episodesCount: kissKhDetail?.episodesCount || 0,\n        });\n      if (findBilibili && findBilibili.id) {\n        provider.push({\n          id: findBilibili.id.toString(),\n          provider: 'Bilibili',\n          episodesCount: bilibiliDetail?.totalEpisodes || 0,\n        });\n      }\n      if (loklokSearch && loklokSearch?.data?.id)\n        provider.push({\n          id: loklokSearch?.data?.id,\n          provider: 'Loklok',\n          episodesCount: loklokDetail?.episodeVo ? loklokDetail.episodeVo.length : 0,\n        });\n      return provider;\n    }\n  };\n  const results = await cachified({\n    key: `${type}-${title}-${orgTitle}-${year}-${season}-${animeId}-${animeType}-${isEnded}`,\n    ttl: 1000 * 60 * 60 * 24,\n    staleWhileRevalidate: 1000 * 60 * 60 * 24 * 14,\n    cache: lruCache,\n    request: undefined,\n    getFreshValue: async () => getProviders(),\n    checkValue: (value) => {\n      if (typeof value === 'undefined') return false;\n      return true;\n    },\n    forceFresh: !isEnded, // force fresh if is not ended\n    fallbackToCache: 1000 * 60 * 60 * 24, // fallback to cache if fresh value is not available\n  });\n  return results;\n};\n\nexport default getProviderList;\n"
  },
  {
    "path": "app/services/supabase/auth.server.ts",
    "content": "import { redirect } from '@remix-run/node';\nimport type { Session } from '@supabase/supabase-js';\nimport isbot from 'isbot';\n\nimport sgConfigs from '../configs.server';\nimport supabase from './client.server';\nimport { commitAuthCookie, getSessionFromCookie } from './cookie.server';\n\nexport const signUp = async (email: string, password: string) =>\n  supabase.auth.signUp({\n    email,\n    password,\n  });\n\nexport const signInWithPassword = async (email: string, password: string) =>\n  supabase.auth.signInWithPassword({\n    email,\n    password,\n  });\n\nexport const getUserFromCookie = async (cookie: string) => {\n  const authCookie = await getSessionFromCookie(cookie);\n  if (authCookie.has('auth_token')) {\n    const authToken = authCookie.get('auth_token');\n    const user = (await supabase.auth.getUser(authToken.access_token)).data.user || undefined;\n\n    return user;\n  }\n};\n\nexport async function requestPayload(req: Request) {\n  if (!process.env.REQ_ENCODE_ATTRS) {\n    console.error('Please make sure you have REQ_ENCODE_ATTRS in your .env');\n    process.exit();\n  }\n\n  const payloadAttrs = process.env.REQ_ENCODE_ATTRS.split('.');\n  return payloadAttrs.map((attr) => req.headers.get(attr)).join('');\n}\n\nexport async function authenticate(\n  request: Request,\n  customAuthRequired?: boolean,\n  botcheckRequired?: boolean,\n  payloadCheckRequired?: boolean,\n  headers = new Headers(),\n) {\n  const isbotAuth = isbot.spawn();\n\n  isbotAuth.exclude([\n    'Checkly',\n    'Checkly, https://www.checklyhq.com',\n    'Checkly/1.0 (https://www.checklyhq.com)',\n    'chrome-lighthouse',\n    'googlebot',\n    'googlebot/2.1 (+http://www.google.com/bot.html)',\n    'bingbot',\n    'bingbot/2.0 (+http://www.bing.com/bingbot.htm)',\n    'discordbot',\n    'Discordbot/2.0',\n    'twitterbot',\n    'Twitterbot/1.0',\n    'vercel',\n    'Vercel/1.0 (https://vercel.com/docs/bots)',\n  ]);\n  // try to get the session (from cookie) and payload from request\n  const [session, payload, botcheck] = await Promise.all([\n    getSessionFromCookie(request.headers.get('Cookie')),\n    process.env.NODE_ENV === 'production' ? requestPayload(request) : undefined,\n    isbotAuth(request.headers.get('User-Agent')),\n  ]);\n\n  if (botcheck && botcheckRequired) {\n    console.log('bot detected', request.headers.get('User-Agent'));\n    throw new Response(null, { status: 500 });\n  } else if (!session.has('auth_token')) {\n    // there is no token, no signed-in or expired cookie\n    if (sgConfigs.__globalAuthRequired || customAuthRequired) {\n      // if global auth is required, redirect to /sign-in\n      const url = new URL(request.url);\n      const ref = (url.pathname + url.search).replace('?', '_0x3F_').replace('&', '_0x26');\n      throw redirect(`/sign-in?ref=${ref}`);\n    }\n  } else {\n    // there is some access token in cookie session\n    const authToken = session.get('auth_token');\n\n    if (\n      payload !== authToken?.req_payload &&\n      process.env.NODE_ENV === 'production' &&\n      payloadCheckRequired\n    ) {\n      // the access token and the agent does not come from same agent\n      throw new Response(null, { status: 200 });\n    }\n\n    const {\n      data: { user },\n    } = await supabase.auth.getUser(authToken.access_token);\n\n    // if (getUserError) throw getUserError;\n\n    if (!user || authToken.expires_at < Date.now()) {\n      // invalid token or access_token has been expired\n      // refresh the session\n      const { data } = await supabase.auth.setSession(authToken as Session);\n\n      // if (setSessionError) throw setSessionError;\n\n      if (data.session) {\n        session.set('auth_token', {\n          access_token: data.session.access_token,\n          refresh_token: data.session.refresh_token,\n          expires_at: Date.now() + (data.session.expires_in - 10) * 1000,\n          req_payload:\n            process.env.NODE_ENV === 'production' ? await requestPayload(request) : undefined,\n        });\n\n        headers.append('Set-Cookie', await commitAuthCookie(session));\n\n        if (request.method === 'GET') {\n          // redirect to the same url if loader (GET)\n          throw redirect(request.url, { headers });\n        }\n      }\n    } else {\n      // if there is a valid user, return user\n      return user;\n    }\n  }\n}\n"
  },
  {
    "path": "app/services/supabase/client.server.ts",
    "content": "import { env } from 'process';\nimport { createClient } from '@supabase/supabase-js';\n\nimport type { Database } from './table.server';\n\nlet supabaseUrl;\nlet supabaseKey;\n\nif (env.NODE_ENV === 'production') {\n  if (!env.SUPABASE_URL) {\n    throw new Error('SUPABASE_URL is required');\n  }\n  supabaseUrl = env.SUPABASE_URL;\n\n  if (!env.SUPABASE_SERVICE_KEY) {\n    throw new Error('SUPABASE_SERVICE_KEY is required');\n  }\n  supabaseKey = env.SUPABASE_SERVICE_KEY;\n} else {\n  if (!env.SUPABASE_DEV_URL) {\n    throw new Error('SUPABASE_DEV_URL is required');\n  }\n  supabaseUrl = env.SUPABASE_DEV_URL;\n\n  if (!env.SUPABASE_DEV_KEY) {\n    throw new Error('SUPABASE_DEV_KEY is required');\n  }\n  supabaseKey = env.SUPABASE_DEV_KEY;\n}\n\nconst options = {\n  auth: {\n    autoRefreshToken: true,\n    persistSession: true,\n    detectSessionInUrl: true,\n  },\n  global: {\n    headers: {\n      'x-my-custom-header': env.NODE_ENV === 'production' ? 'remix-movie' : 'remix-movie-dev',\n    },\n  },\n};\n\nconst supabase = createClient<Database>(supabaseUrl, supabaseKey, options);\n\nexport default supabase;\n"
  },
  {
    "path": "app/services/supabase/cookie.server.ts",
    "content": "import { env } from 'process';\nimport { createCookieSessionStorage } from '@remix-run/node';\n\nconst {\n  getSession: getSessionFromCookie,\n  commitSession: commitAuthCookie,\n  destroySession: destroyAuthCookie,\n} = createCookieSessionStorage({\n  cookie: {\n    name: 'remix-movie-auth',\n    httpOnly: true,\n    maxAge: env.NODE_ENV === 'production' ? 3600 * 24 * 30 : 3600 * 2, // about 1 month\n    path: '/',\n    sameSite: 'lax',\n    secrets: [env.SESSION_KEY || 's3cret1'],\n    secure: env.NODE_ENV === 'production',\n  },\n});\n\nexport { getSessionFromCookie, commitAuthCookie, destroyAuthCookie };\n"
  },
  {
    "path": "app/services/supabase/index.ts",
    "content": "export { default as supabase } from './client.server';\nexport * from './auth.server';\nexport * from './cookie.server';\nexport * from './table.server';\n"
  },
  {
    "path": "app/services/supabase/table.server.ts",
    "content": "import supabase from './client.server';\n\nexport interface IHistoryDTO {\n  user_id: string;\n  media_type: 'movie' | 'tv' | 'anime';\n  duration: number; // in seconds\n  watched: number;\n  route: string;\n  media_id: string;\n  season?: string;\n  episode?: string;\n  poster?: string;\n  title?: string;\n  overview?: string;\n}\n\nexport interface IHistory extends IHistoryDTO {\n  id: number;\n  created_at: Date;\n  updated_at: Date;\n}\n\nexport interface Database {\n  public: {\n    Tables: {\n      histories: {\n        Row: IHistory;\n        Insert: IHistoryDTO;\n      };\n    };\n  };\n}\n\nexport async function insertHistory(_history: IHistoryDTO) {\n  try {\n    const query = supabase\n      .from('histories')\n      .select()\n      .eq('user_id', _history.user_id)\n      .eq('media_id', _history.media_id)\n      .eq('media_type', _history.media_type);\n\n    if (_history.season) {\n      query.eq('season', _history.season);\n    }\n\n    if (_history.episode) {\n      query.eq('episode', _history.episode);\n    }\n\n    const { data: oldData } = await query;\n\n    if (oldData && oldData.length > 0) {\n      const { error } = await supabase\n        .from('histories')\n        .update({ ..._history })\n        .eq('id', oldData[0].id);\n\n      if (!error) return { data: oldData as IHistory[], error };\n\n      //\n    } else {\n      const { data, error } = await supabase.from('histories').insert(_history).select();\n\n      if (data) {\n        // console.log(data);\n        return { data: data as IHistory[], error };\n      }\n\n      if (error) {\n        console.log(error);\n      }\n    }\n\n    return { data: null, error: null };\n  } catch (error) {\n    console.error(error);\n    return { data: null, error };\n  }\n}\n\nexport async function getHistory(\n  _userId: string,\n  types: string | null,\n  from: string | null,\n  to: string | null,\n  _page = 1,\n) {\n  try {\n    const query = supabase.from('histories').select().eq('user_id', _userId);\n\n    if (types) {\n      query.in('media_type', types.split(','));\n    }\n    if (from) {\n      query.gte('updated_at', new Date(from).toISOString());\n    }\n    if (to) {\n      const date = new Date(to);\n      date.setDate(date.getDate() + 1);\n      query.lte('updated_at', date.toISOString());\n    }\n\n    query.order('updated_at', { ascending: false }).range((_page - 1) * 20, _page * 20 - 1);\n\n    const { data, error } = await query;\n\n    if (data) {\n      // console.log(data);\n      return data as IHistory[];\n    }\n\n    console.error(error);\n    return [];\n  } catch (error) {\n    console.error(error);\n    return [];\n  }\n}\n\nexport async function getCountHistory(\n  _userId: string,\n  types: string | null,\n  from: string | null,\n  to: string | null,\n) {\n  try {\n    const query = supabase\n      .from('histories')\n      .select('*', { count: 'exact', head: true })\n      .eq('user_id', _userId);\n\n    if (types) {\n      query.in('media_type', types.split(','));\n    }\n    if (from) {\n      query.gte('updated_at', new Date(from).toISOString());\n    }\n    if (to) {\n      const date = new Date(to);\n      date.setDate(date.getDate() + 1);\n      query.lte('updated_at', date.toISOString());\n    }\n    const { count, error } = await query;\n\n    if (count) {\n      return count;\n    }\n    console.error(error);\n    return 0;\n  } catch (error) {\n    console.error(error);\n    return 0;\n  }\n}\n"
  },
  {
    "path": "app/services/supabase/tables.sql",
    "content": "CREATE TABLE histories (\n    id bigint generated BY\n        DEFAULT AS identity PRIMARY KEY,\n        created_at timestamp WITH time zone DEFAULT timezone(\n            'utc'::text, now(\n)\n) NOT NULL,\n        updated_at timestamp WITH time zone DEFAULT timezone(\n            'utc'::text, now(\n)\n) NOT NULL,\n        user_id uuid NOT NULL,\n        media_type varchar NOT NULL,\n        media_id varchar NOT NULL,\n        duration bigint NOT NULL,\n        title varchar,\n        route varchar NOT NULL,\n        watched bigint,\n        poster varchar,\n        overview text,\n        season varchar,\n        episode varchar\n);\n"
  },
  {
    "path": "app/services/tmdb/tmdb.server.ts",
    "content": "import { META, PROVIDERS_LIST } from '@consumet/extensions';\n\nimport type { IMedia } from '~/types/media';\nimport { cachified, fetcher, lruCache } from '~/utils/server/cache.server';\n\nimport type {\n  ICredit,\n  IDetailImages,\n  ILanguage,\n  IList,\n  IListGenre,\n  IMediaList,\n  IMovieDetail,\n  IMovieTranslations,\n  IPeopleCredits,\n  IPeopleDetail,\n  IPeopleExternalIds,\n  IPeopleImages,\n  ISeasonDetail,\n  ITvShowDetail,\n  IVideos,\n  ListMovieType,\n  ListPersonType,\n  ListTvShowType,\n  MediaType,\n  TimeWindowType,\n} from './tmdb.types';\nimport { postFetchDataHandler, TMDB } from './utils.server';\n\n// reusable function\nconst getListFromTMDB = async (\n  url: string,\n  type?: 'movie' | 'tv' | 'people',\n): Promise<IMediaList> => {\n  try {\n    const urlBase = new URL(url);\n    const fetched = await fetcher<{\n      items?: IMedia[];\n      page: number;\n      total_pages: number;\n      total_results: number;\n    }>({\n      url,\n      key: `tmdb${type ? `-${type}` : ''}${urlBase.pathname.replace(/\\//g, '-')}${urlBase.search\n        .replace(/\\?/g, '-')\n        .replace(/=/g, '_')\n        .replace(/&/g, '-')}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n    return {\n      page: fetched.page,\n      totalPages: fetched.total_pages,\n      items: [...postFetchDataHandler(fetched, type)],\n      totalResults: fetched.total_results,\n    } as IMediaList;\n  } catch (error) {\n    console.error(error);\n    return { page: 0, totalPages: 0, items: [], totalResults: 0 };\n  }\n};\n\n/* ============================================Config Field=========================================== */\n\nexport const getListLanguages = async (): Promise<ILanguage[] | undefined> => {\n  try {\n    const fetched = await fetcher<ILanguage[]>({\n      url: TMDB.languagesUrl(),\n      key: 'tmdb-languages',\n      ttl: 1000 * 60 * 60 * 24 * 30,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 365,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.error(error);\n  }\n};\n\n/* =======================================End of Config Field========================================= */\n\n/* ===========================================Trending Field========================================== */\n\n// get a list of trending items\nexport const getTrending = async (\n  mediaType: MediaType,\n  timeWindow: TimeWindowType,\n  language?: string,\n  page?: number,\n): Promise<IMediaList> => {\n  const url = TMDB.trendingUrl(mediaType, timeWindow, language, page);\n  return getListFromTMDB(url, mediaType === 'person' ? 'people' : undefined);\n};\n\n/* ======================================End of Trending Field======================================== */\n\n/* ===========================================Movie Field============================================= */\n\n/**\n * It fetches a list of movies from the TMDB API, and returns a list of movies\n * @param {ListMovieType} type - ListMovieType\n * @param {number} [page] - number\n * @returns An object with the following properties:\n * page: number\n * totalPages: number\n * items: IMovie[]\n */\nexport const getListMovies = async (\n  type: ListMovieType,\n  language?: string,\n  page?: number,\n): Promise<IMediaList> => {\n  const url = TMDB.listMoviesUrl(type, page, language);\n  return getListFromTMDB(url, 'movie');\n};\n\n/**\n * It fetches a movie detail from the TMDB API and returns the data if it exists, otherwise it returns\n * undefined\n * @param {number} id - number - The id of the movie you want to get the details for\n * @returns A Promise that resolves to an IMovieDetail or undefined.\n */\nexport const getMovieDetail = async (\n  id: number,\n  language?: string,\n): Promise<IMovieDetail | undefined> => {\n  try {\n    const fetched = await fetcher<IMovieDetail>({\n      url: TMDB.movieDetailUrl(id, language),\n      key: `tmdb-movie-detail-${id}-${language}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.error(error);\n  }\n};\n\n/* =====================================End of Movie Field============================================ */\n\n/* ========================================Tv Show Field============================================== */\n\n/**\n * It takes a type and a page number, and returns a promise that resolves to an object that contains a\n * list of tv shows\n * @param {ListTvShowType} type - ListTvShowType = 'airing_today' | 'on_the_air' | 'popular' |\n * 'top_rated';\n * @param {number} [page] - number\n * @returns A promise that resolves to an object of type IMediaList.\n */\nexport const getListTvShows = async (\n  type: ListTvShowType,\n  language?: string,\n  page?: number,\n): Promise<IMediaList> => {\n  const url = TMDB.listTvShowsUrl(type, page, language);\n  return getListFromTMDB(url, 'tv');\n};\n\nexport const getTvShowDetail = async (\n  id: number,\n  language?: string,\n): Promise<ITvShowDetail | undefined> => {\n  try {\n    const fetched = await fetcher<ITvShowDetail>({\n      url: TMDB.tvShowDetailUrl(id, language),\n      key: `tmdb-tv-show-detail-${id}-${language}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.error(error);\n  }\n};\n\n/**\n * It fetches the IMDB ID of a TV show from TMDB, and if it doesn't exist, it throws an error\n * @param {number} id - number - The TV show ID from TMDB\n * @returns A Promise that resolves to a number or undefined.\n */\nexport const getTvShowIMDBId = async (id: number): Promise<string | undefined> => {\n  try {\n    const fetched = await fetcher<{ imdb_id: string | undefined }>({\n      url: TMDB.tvExternalIds(id),\n      key: `tmdb-tv-show-imdb-id-${id}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n\n    if (!fetched?.imdb_id) throw new Error('This TV show does not have IMDB ID');\n\n    return fetched.imdb_id;\n  } catch (error) {\n    console.error(error);\n  }\n};\n\n// Season and episodes\n\nexport const getTvSeasonDetail = async (\n  tv_id: number,\n  season_number: number,\n  language?: string,\n): Promise<ISeasonDetail | undefined> => {\n  try {\n    const fetched = await fetcher<ISeasonDetail>({\n      url: TMDB.tvSeasonDetailUrl(tv_id, season_number, language),\n      key: `tmdb-tv-season-detail-${tv_id}-${season_number}-${language}`,\n      ttl: 1000 * 60 * 60,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.error(error);\n  }\n};\n\nexport const getTvSeasonCredits = async (\n  tv_id: number,\n  season_number: number,\n  language?: string,\n): Promise<ICredit | undefined> => {\n  try {\n    const fetched = await fetcher<ICredit>({\n      url: TMDB.tvSeasonCreditsUrl(tv_id, season_number, language),\n      key: `tmdb-tv-season-credits-${tv_id}-${season_number}-${language}`,\n      ttl: 1000 * 60 * 60 * 24 * 7,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 30,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.error(error);\n  }\n};\n\nexport const getTvSeasonVideos = async (\n  tv_id: number,\n  season_number: number,\n  language?: string,\n): Promise<IVideos | undefined> => {\n  try {\n    const fetched = await fetcher<IVideos>({\n      url: TMDB.tvSeasonVideosUrl(tv_id, season_number, language),\n      key: `tmdb-tv-season-videos-${tv_id}-${season_number}-${language}`,\n      ttl: 1000 * 60 * 60 * 24 * 7,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 30,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.error(error);\n  }\n};\n\nexport const getTvSeasonImages = async (\n  tv_id: number,\n  season_number: number,\n  language?: string,\n): Promise<IDetailImages | undefined> => {\n  try {\n    const fetched = await fetcher<IDetailImages>({\n      url: TMDB.tvSeasonImagesUrl(tv_id, season_number, language),\n      key: `tmdb-tv-season-images-${tv_id}-${season_number}-${language}`,\n      ttl: 1000 * 60 * 60 * 24 * 7,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 30,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.error(error);\n  }\n};\n\n/* ======================================End of Tv Show Field========================================= */\n\n/* ==========================================People Field============================================= */\nexport const getListPeople = async (\n  type: ListPersonType,\n  language?: string,\n  page?: number,\n): Promise<IMediaList | undefined> => {\n  const url = TMDB.listPerson(type, language, page);\n  return getListFromTMDB(url, 'people');\n};\n\nexport const getPeopleDetail = async (\n  person_id: number,\n  language?: string,\n): Promise<IPeopleDetail | undefined> => {\n  try {\n    const fetched = await fetcher<IPeopleDetail>({\n      url: TMDB.personDetail(person_id, language),\n      key: `tmdb-people-detail-${person_id}-${language}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n    if (!fetched) throw new Error('Dont have result');\n    return fetched;\n  } catch (error) {\n    console.error(error);\n  }\n};\n\nexport const getPeopleExternalIds = async (\n  person_id: number,\n  language?: string,\n): Promise<IPeopleExternalIds | undefined> => {\n  try {\n    const fetched = await fetcher<IPeopleExternalIds>({\n      url: TMDB.peopleExternalIds(person_id, language),\n      key: `tmdb-people-external-ids-${person_id}-${language}`,\n      ttl: 1000 * 60 * 60 * 24 * 7,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 30,\n      cache: lruCache,\n    });\n    if (!fetched) throw new Error('Dont have result');\n    return fetched;\n  } catch (error) {\n    console.error(error);\n  }\n};\n\nexport const getPeopleImages = async (\n  person_id: number,\n  language?: string,\n): Promise<IPeopleImages | undefined> => {\n  try {\n    const fetched = await fetcher<IPeopleImages>({\n      url: TMDB.peopleImages(person_id, language),\n      key: `tmdb-people-images-${person_id}-${language}`,\n      ttl: 1000 * 60 * 60 * 24 * 7,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 30,\n      cache: lruCache,\n    });\n    if (!fetched) throw new Error('Dont have result');\n    return fetched;\n  } catch (error) {\n    console.error(error);\n  }\n};\n\nexport const getPeopleCredits = async (\n  id: number,\n  type?: 'movie' | 'tv' | 'combined',\n  language?: string,\n): Promise<IPeopleCredits | undefined> => {\n  try {\n    const fetched = await fetcher<IPeopleCredits>({\n      url: TMDB.peopleCredits(id, type, language),\n      key: `tmdb-people-credits-${id}-${type}-${language}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n    return {\n      cast: fetched?.cast ? postFetchDataHandler(fetched.cast) : [],\n      id: fetched?.id || undefined,\n    };\n  } catch (error) {\n    console.error(error);\n  }\n};\n\n/* ======================================End of People Field========================================== */\n\n/* ==========================================Search Field============================================= */\n\nexport const getSearchMovies = async (\n  keyword: string,\n  page?: number,\n  language?: string,\n  include_adult?: boolean,\n  region?: string,\n  year?: number,\n): Promise<IMediaList> => {\n  const url = TMDB.searchMovies(keyword, language, page, include_adult, region, year, undefined);\n  return getListFromTMDB(url, 'movie');\n};\n\nexport const getSearchTvShows = async (\n  keyword: string,\n  page?: number,\n  language?: string,\n  include_adult?: boolean,\n  first_air_date_year?: number,\n): Promise<IMediaList> => {\n  const url = TMDB.searchTv(keyword, language, page, include_adult, first_air_date_year);\n  return getListFromTMDB(url, 'tv');\n};\n\nexport const getSearchPerson = async (\n  keyword: string,\n  page?: number,\n  include_adult?: boolean,\n  language?: string,\n  region?: string,\n): Promise<IMediaList | undefined> => {\n  const url = TMDB.searchPerson(keyword, page, include_adult, language, region);\n  return getListFromTMDB(url, 'people');\n};\n\n/* =======================================End of Search Field========================================= */\n\n/* =============================================UTILS================================================= */\n\n/**\n * It fetches a video from the TMDB API and returns the response\n * @param {'movie' | 'tv'} type - 'movie' | 'tv'\n * @param {number} id - number - the id of the movie or tv show\n * @returns The return type is a Promise of IVideos or undefined.\n */\nexport const getVideos = async (type: 'movie' | 'tv', id: number): Promise<IVideos | undefined> => {\n  try {\n    const fetched = await fetcher<IVideos>({\n      url: TMDB.videoUrl(type, id),\n      key: `tmdb-video-${type}-${id}`,\n      ttl: 1000 * 60 * 60 * 24 * 7,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 30,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.error(error);\n  }\n};\n\n/**\n * It fetches a credit object from the TMDB API, and returns it if it exists\n * @param {'movie' | 'tv'} type - 'movie' | 'tv'\n * @param {number} id - number,\n * @returns Promise&lt;ICredit | undefined&gt;\n */\nexport const getCredits = async (\n  type: 'movie' | 'tv',\n  id: number,\n): Promise<ICredit | undefined> => {\n  try {\n    const fetched = await fetcher<ICredit>({\n      url: TMDB.creditUrl(type, id),\n      key: `tmdb-credit-${type}-${id}`,\n      ttl: 1000 * 60 * 60 * 24 * 7,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 30,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.error(error);\n  }\n};\n\nexport const getImages = async (\n  type: 'movie' | 'tv',\n  id: number,\n  language?: string,\n): Promise<IDetailImages | undefined> => {\n  try {\n    const fetched = await fetcher<IDetailImages>({\n      url: TMDB.imagesUrl(type, id, language),\n      key: `tmdb-images-${type}-${id}-${language}`,\n      ttl: 1000 * 60 * 60 * 24 * 7,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 30,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.error(error);\n  }\n};\n\n/**\n * It takes a type and an id, and returns a promise that resolves to a list of media\n * @param {'movie' | 'tv'} type - 'movie' | 'tv'\n * @param {number} id - The id of the movie or tv show\n * @returns A promise that resolves to an IMediaList object.\n */\nexport const getSimilar = async (\n  type: 'movie' | 'tv',\n  id: number,\n  page?: number,\n  language?: string,\n): Promise<IMediaList> => {\n  const url = TMDB.similarUrl(type, id, page, language);\n  return getListFromTMDB(url);\n};\n\nexport const getRecommendation = async (\n  type: 'movie' | 'tv',\n  id: number,\n  page?: number,\n  language?: string,\n): Promise<IMediaList> => {\n  const url = TMDB.recommendationUrl(type, id, page, language);\n  return getListFromTMDB(url);\n};\n\nexport const getListGenre = async (\n  type: 'movie' | 'tv',\n  language?: string,\n): Promise<{ [id: string]: string }> => {\n  const url = TMDB.listGenre(type, language);\n  try {\n    const fetched = await fetcher<IListGenre>({\n      url,\n      key: `tmdb-genre-${type}-${language}`,\n      ttl: 1000 * 60 * 60 * 24 * 30,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 365,\n      cache: lruCache,\n    });\n    const result: { [key: string]: string } = {};\n\n    for (let i = 0; i < fetched.genres.length; i += 1) {\n      const genre = fetched.genres[i];\n      result[genre.id] = genre.name;\n    }\n    return result;\n  } catch (error) {\n    console.error(error);\n    return {};\n  }\n};\n\nexport const getListDiscover = async (\n  type: 'movie' | 'tv',\n  with_genres?: string,\n  sort_by?: string,\n  language?: string,\n  page?: number,\n  releaseDateGte?: string,\n  releaseDateLte?: string,\n  firstAirDateGte?: string,\n  firstAirDateLte?: string,\n  withOriginalLanguage?: string,\n  voteCountGte?: number,\n  voteAverageGte?: number,\n  voteAverageLte?: number,\n  withCast?: string,\n  withCrew?: string,\n  withPeople?: string,\n  withCompanies?: string,\n  withNetworks?: string,\n  withKeywords?: string,\n  withRuntimeGte?: number,\n  withRuntimeLte?: number,\n  withStatus?: string,\n  withType?: string,\n  airDateGte?: string,\n  airDateLte?: string,\n): Promise<IMediaList> => {\n  const url = TMDB.discoverUrl(\n    type,\n    with_genres,\n    sort_by,\n    language,\n    page,\n    type === 'movie' ? releaseDateGte : undefined,\n    type === 'movie' ? releaseDateLte : undefined,\n    type === 'tv' ? firstAirDateGte : undefined,\n    type === 'tv' ? firstAirDateLte : undefined,\n    withOriginalLanguage,\n    voteCountGte,\n    voteAverageGte,\n    voteAverageLte,\n    type === 'movie' ? withCast : undefined,\n    type === 'movie' ? withCrew : undefined,\n    type === 'movie' ? withPeople : undefined,\n    withCompanies,\n    type === 'tv' ? withNetworks : undefined,\n    withKeywords,\n    withRuntimeGte,\n    withRuntimeLte,\n    type === 'tv' ? withStatus : undefined,\n    type === 'tv' ? withType : undefined,\n    type === 'tv' ? airDateGte : undefined,\n    type === 'tv' ? airDateLte : undefined,\n  );\n\n  return getListFromTMDB(url, type);\n};\n\nexport const getTranslations = async (\n  type: 'movie' | 'tv',\n  id: number,\n): Promise<IMovieTranslations | undefined> => {\n  try {\n    const fetched = await fetcher<IMovieTranslations>({\n      url: TMDB.translationsUrl(type, id),\n      key: `tmdb-translations-${type}-${id}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n    return fetched;\n  } catch (error) {\n    console.error(error);\n  }\n};\n\nexport const getListDetail = async (\n  listId: number,\n  language?: string,\n): Promise<IList | undefined> => {\n  try {\n    const fetched = await fetcher<IList>({\n      url: TMDB.listUrl(listId, language),\n      key: `tmdb-list-${listId}-${language}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n    return {\n      ...fetched,\n      items: [...postFetchDataHandler(fetched.items)],\n    };\n  } catch (error) {\n    console.error(error);\n  }\n};\n\nexport const getImdbRating = async (\n  id: string,\n): Promise<{ count: number; star: number } | undefined> => {\n  try {\n    const fetched = await fetcher<{\n      rating: { count: number; star: number };\n    }>({\n      url: TMDB.imdbDetailUrl(id),\n      key: `tmdb-imdb-${id}`,\n      ttl: 1000 * 60 * 60 * 24,\n      staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n      cache: lruCache,\n    });\n    if (fetched && fetched.rating) {\n      return fetched.rating;\n    }\n  } catch (error) {\n    console.error(error);\n  }\n};\n\nexport const getInfoWithProvider = async (id: string, type: 'movie' | 'tv', provider?: string) => {\n  let tmdb = new META.TMDB(TMDB.key);\n\n  if (provider) {\n    const possibleProvider = PROVIDERS_LIST.MOVIES.find(\n      (p) => p.name.toLowerCase() === provider.toLowerCase(),\n    );\n    if (!possibleProvider) {\n      throw new Error(`Provider ${provider} not found`);\n    }\n    tmdb = new META.TMDB(TMDB.key, possibleProvider);\n  }\n\n  const info = await cachified({\n    key: `tmdb-${type}-${id}-info`,\n    ttl: 1000 * 60 * 60 * 24,\n    staleWhileRevalidate: 1000 * 60 * 60 * 24 * 7,\n    cache: lruCache,\n    request: undefined,\n    getFreshValue: async () => {\n      try {\n        const res = await tmdb.fetchMediaInfo(id, type);\n        return res;\n      } catch (error) {\n        console.error(error);\n        return undefined;\n      }\n    },\n  });\n\n  return info;\n};\n\nexport const getWatchEpisode = async (id: string, episodeId: string) => {\n  const tmdb = new META.TMDB(TMDB.key);\n\n  const data = await cachified({\n    key: `tmdb-${id}-episode-${episodeId}-watch`,\n    ttl: 1000 * 60 * 60,\n    staleWhileRevalidate: 1000 * 60 * 60 * 3,\n    cache: lruCache,\n    request: undefined,\n    getFreshValue: async () => {\n      try {\n        const res = await tmdb.fetchEpisodeSources(episodeId, id);\n        return res;\n      } catch (error) {\n        console.error(error);\n        return undefined;\n      }\n    },\n  });\n  return data;\n};\n"
  },
  {
    "path": "app/services/tmdb/tmdb.types.ts",
    "content": "import type { IMedia } from '~/types/media';\n\n// https://developers.themoviedb.org/3/configuration/get-api-configuration\nexport type PosterSize = 'w92' | 'w154' | 'w185' | 'w342' | 'w500' | 'w780' | 'original';\nexport type BackdropSize = 'w300' | 'w780' | 'w1280' | 'original';\nexport type LogoSize = 'w45' | 'w92' | 'w154' | 'w185' | 'w300' | 'w500' | 'original';\nexport type ProfileSize = 'w45' | 'w185' | 'h632' | 'original';\nexport type StillSize = 'w92' | 'w185' | 'w300' | 'original';\n\nexport type MediaType = 'all' | 'movie' | 'tv' | 'person';\nexport type TimeWindowType = 'day' | 'week';\nexport type ListMovieType = 'upcoming' | 'top_rated' | 'popular' | 'now_playing';\nexport type ListTvShowType = 'on_the_air' | 'popular' | 'top_rated' | 'airing_today';\nexport type ListPersonType = 'popular' | 'latest';\n\n/**\n * Media here means a tv show or a movie\n * Here is just some common fields\n * this could be extended in the future to create more specific interface\n */\n\nexport interface IMediaList {\n  items?: IMedia[];\n  page: number;\n  totalPages: number;\n  totalResults: number;\n}\n\nexport interface IMovieDetail {\n  adult?: boolean;\n  backdrop_path?: string | null;\n  belongs_to_collection?: null | object;\n  budget?: number;\n  genres?: {\n    id?: number;\n    name?: string;\n  }[];\n  homepage?: string | null;\n  id?: number;\n  imdb_id?: string | null;\n  original_language?: string;\n  original_title?: string;\n  overview?: string | null;\n  popularity?: number;\n  poster_path?: string | null;\n  production_companies?: {\n    name?: string;\n    id?: number;\n    logo_path?: string | null;\n    original_country?: string;\n  }[];\n  production_countries?: {\n    iso_3166_1?: string;\n    name?: string;\n  }[];\n  release_date?: string | number | Date; // data\n  revenue?: number;\n  runtime?: number | null;\n  spoken_languages?: {\n    english_name?: string;\n    iso_639_1?: string;\n    name?: string;\n  }[];\n  status?: 'Rumored' | 'Planned' | 'In Production' | 'Post Production' | 'Released' | 'Canceled';\n  tagline?: string | null;\n  title?: string;\n  titleEng?: string;\n  video?: boolean;\n  vote_average?: number;\n  vote_count?: number;\n}\n\nexport interface IGenre {\n  id: number;\n  name: string;\n}\n\nexport interface IListGenre {\n  genres: IGenre[];\n}\n\nexport interface ICredit {\n  id: number;\n  cast: IPeople[];\n  crew: IPeople[];\n}\n\nexport interface IVideos {\n  id: number;\n  results: {\n    iso_639_1: string;\n    iso_3166_1: string;\n    name: string;\n    key: string; // https://www.youtube.com/watch?v=${key}\n    site: string;\n    size: number;\n    type: string;\n    official: boolean;\n    published_at: string;\n    id: string;\n  }[];\n}\n\nexport interface ITvShowDetail {\n  backdrop_path?: string | null;\n  created_by?: {\n    id?: number;\n    credit_id?: string;\n    name?: string;\n    gender?: number;\n    profile_path?: string | null;\n  }[];\n  episode_run_time?: number[];\n  first_air_date?: string | number | Date;\n  genres?: {\n    id?: number;\n    name?: string;\n  }[];\n  homepage?: string;\n  id?: number;\n  in_production?: boolean;\n  languages?: string[];\n  last_air_date?: string;\n  last_episode_to_air?: {\n    air_date?: string;\n    episode_number?: number;\n    id?: number;\n    name?: string;\n    overview?: string;\n    production_code?: string;\n    season_number?: number;\n    still_path?: string | null;\n    vote_average?: number;\n    vote_count?: number;\n  };\n  name?: string;\n  nameEng?: string;\n  next_episode_to_air?: null;\n  networks?: {\n    name?: string;\n    id?: number;\n    logo_path?: string | null;\n    origin_country?: string;\n  }[];\n  number_of_episodes?: number;\n  number_of_seasons?: number;\n  origin_country?: string[];\n  original_language?: string;\n  original_name?: string;\n  overview?: string;\n  popularity?: number;\n  poster_path?: string | null;\n  production_companies?: {\n    name?: string;\n    id?: number;\n    logo_path?: string | null;\n    original_country?: string;\n  }[];\n  production_countries?: {\n    iso_3166_1?: string;\n    name?: string;\n  }[];\n  seasons?: {\n    air_date?: string;\n    episode_count?: number;\n    id?: number;\n    name?: string;\n    overview?: string;\n    poster_path?: string;\n    season_number?: number;\n  }[];\n  spoken_languages?: {\n    english_name?: string;\n    iso_639_1?: string;\n    name?: string;\n  }[];\n  status?: string;\n  tagline?: string | null;\n  type?: string;\n  vote_average?: number;\n  vote_count?: number;\n}\n\nexport interface IPeople {\n  adult?: boolean;\n  id?: number;\n  known_for?: IMedia[];\n  name?: string;\n  popularity?: number;\n  profile_path?: string;\n  gender?: number;\n  known_for_department?: string;\n  character?: string;\n  credit_id?: string;\n  order?: number;\n  cast_id?: number;\n  original_name?: string;\n  department?: string;\n  job?: string;\n}\n\nexport interface IListPeople {\n  page: number;\n  results: IMedia[];\n  totalPages: number;\n  totalResults: number;\n}\n\nexport interface IPeopleDetail {\n  birthday?: string;\n  known_for_department?: string;\n  deathday?: null | string;\n  id?: number;\n  name?: string;\n  also_known_as?: string[];\n  gender?: number;\n  biography?: string;\n  popularity?: number;\n  place_of_birth?: string;\n  profile_path?: string;\n  adult?: boolean;\n  imdb_id?: string;\n  homepage?: string;\n}\n\nexport interface IPeopleExternalIds {\n  imdb_id?: string;\n  facebook_id?: string;\n  freebase_mid?: string;\n  freebase_id?: string;\n  tvrage_id?: number;\n  twitter_id?: string;\n  id?: number;\n  instagram_id?: string;\n}\n\nexport interface IPeopleImages {\n  id?: number;\n  profiles?: IImage[];\n}\n\nexport interface IPeopleCredits {\n  cast: IMedia[];\n  // crew: {}[];\n  id?: number;\n}\n\nexport interface IDetailImages {\n  id?: number;\n  backdrops?: IImage[];\n  logos?: IImage[];\n  posters?: IImage[];\n}\n\nexport interface IImage {\n  aspect_ratio?: number;\n  file_path?: string;\n  height?: number;\n  iso_639_1?: null | string;\n  vote_average?: number;\n  vote_count?: number;\n  width?: number;\n}\n\nexport interface IMovieTranslations {\n  id: number;\n  translations: ITranslation[];\n}\n\nexport interface ITranslation {\n  data: IDataTranslation;\n  english_name: string;\n  iso_3166_1: string;\n  iso_639_1: string;\n  name: string;\n}\n\nexport interface IDataTranslation {\n  homepage: string;\n  overview: string;\n  runtime: number;\n  tagline: string;\n  title?: string;\n  name?: string;\n}\n\nexport interface ILanguage {\n  english_name: string;\n  iso_639_1: string;\n  name: string;\n}\n\nexport interface ISeasonDetail {\n  _id: string;\n  air_date: Date;\n  episodes: IEpisode[];\n  id: number;\n  name: string;\n  overview: string;\n  poster_path: string;\n  season_number: number;\n}\n\nexport interface IEpisode {\n  air_date: Date;\n  crew: IPeople[];\n  episode_number: number;\n  guest_stars: IPeople[];\n  id: number;\n  name: string;\n  overview: string;\n  production_code: string;\n  runtime: number;\n  season_number: number;\n  show_id: number;\n  still_path: string;\n  vote_average: number;\n  vote_count: number;\n}\n\nexport interface IList {\n  created_by: string;\n  description: string;\n  favorite_count: number;\n  id: string;\n  iso_639_1: string;\n  item_count: number;\n  items: IMedia[];\n  name: string;\n  poster_path: string | null;\n}\n\nexport interface IMovieSource {\n  isM3U8?: boolean;\n  isDASH?: boolean;\n  quality: string;\n  url: string;\n}\n\nexport interface IMovieSubtitle {\n  id?: number;\n  default?: boolean;\n  lang: string;\n  url: string;\n}\n"
  },
  {
    "path": "app/services/tmdb/utils.server.ts",
    "content": "import { env } from 'process';\n\nimport type { IMedia } from '~/types/media';\n\nimport type {\n  BackdropSize,\n  ListMovieType,\n  ListPersonType,\n  ListTvShowType,\n  MediaType,\n  PosterSize,\n  ProfileSize,\n  TimeWindowType,\n} from './tmdb.types';\n\nexport class TMDB {\n  static readonly API_BASE_URL = 'https://api.themoviedb.org/3/';\n\n  static readonly IMDB_API_BASE_URL = env.IMDB_API_URL;\n\n  static readonly MEDIA_BASE_URL = 'https://image.tmdb.org/t/p/';\n\n  static readonly key = process.env.TMDB_API_KEY;\n\n  static trendingUrl = (\n    mediaType: MediaType,\n    timeWindow: TimeWindowType,\n    language?: string,\n    page?: number,\n  ): string => {\n    let url = `${this.API_BASE_URL}trending/${mediaType}/${timeWindow}?api_key=${this.key}`;\n\n    if (language) url += `&language=${language}`;\n    if (page) url += `&page=${page}`;\n\n    return url;\n  };\n\n  static posterUrl = (path: string, size?: PosterSize): string => {\n    if (size) {\n      return `${this.MEDIA_BASE_URL}${size}/${path}`;\n    }\n    return `${this.MEDIA_BASE_URL}original/${path}`;\n  };\n\n  static backdropUrl = (path: string, size?: BackdropSize): string => {\n    if (size) {\n      return `${this.MEDIA_BASE_URL}${size}/${path}`;\n    }\n    return `${this.MEDIA_BASE_URL}${size}/${path}`;\n  };\n\n  static profileUrl = (path: string | undefined, size?: ProfileSize): string => {\n    if (size) {\n      return `${this.MEDIA_BASE_URL}${size}/${path}`;\n    }\n    return `${this.MEDIA_BASE_URL}original/${path}`;\n  };\n\n  static listMoviesUrl = (\n    type: ListMovieType,\n    page?: number,\n    language?: string,\n    region?: string,\n  ): string => {\n    let url = `${this.API_BASE_URL}movie/${type}?api_key=${this.key}`;\n    if (page) {\n      url += `&page=${page}`;\n    }\n    if (language) {\n      url += `&language=${language}`;\n    }\n    if (region) {\n      url += `&region=${region}`;\n    }\n    return url;\n  };\n\n  static movieDetailUrl = (id: number, language?: string): string =>\n    `${this.API_BASE_URL}movie/${id}?api_key=${this.key}&language=${language}`;\n\n  static listTvShowsUrl = (type: ListTvShowType, page?: number, language?: string): string => {\n    let url = `${this.API_BASE_URL}tv/${type}?api_key=${this.key}`;\n    if (page) {\n      url += `&page=${page}`;\n    }\n    if (language) {\n      url += `&language=${language}`;\n    }\n    return url;\n  };\n\n  static tvShowDetailUrl = (id: number, language?: string): string => {\n    let url = `${this.API_BASE_URL}tv/${id}?api_key=${this.key}`;\n    if (language) {\n      url += `&language=${language}`;\n    }\n    return url;\n  };\n\n  static videoUrl = (type: 'movie' | 'tv', id: number, language?: string): string => {\n    let url = `${this.API_BASE_URL}${type}/${id}/videos?api_key=${this.key}`;\n    if (language) {\n      url += `&language=${language}`;\n    }\n    return url;\n  };\n\n  static creditUrl = (type: 'movie' | 'tv', id: number, language?: string): string => {\n    let url = `${this.API_BASE_URL}${type}/${id}/credits?api_key=${this.key}`;\n    if (language) {\n      url += `&language=${language}`;\n    }\n    return url;\n  };\n\n  static imagesUrl = (type: 'movie' | 'tv', id: number, language?: string): string => {\n    let url = `${this.API_BASE_URL}${type}/${id}/images?api_key=${this.key}`;\n    if (language) {\n      url += `&language=${language}`;\n    }\n    return url;\n  };\n\n  static similarUrl = (\n    type: 'movie' | 'tv',\n    id: number,\n    page?: number,\n    language?: string,\n  ): string => {\n    let url = `${this.API_BASE_URL}${type}/${id}/similar?api_key=${this.key}`;\n    if (page) {\n      url += `&page=${page}`;\n    }\n    if (language) {\n      url += `&language=${language}`;\n    }\n    return url;\n  };\n\n  static recommendationUrl = (\n    type: 'movie' | 'tv',\n    id: number,\n    page?: number,\n    language?: string,\n  ): string => {\n    let url = `${this.API_BASE_URL}${type}/${id}/recommendations?api_key=${this.key}`;\n    if (page) {\n      url += `&page=${page}`;\n    }\n    if (language) {\n      url += `&language=${language}`;\n    }\n    return url;\n  };\n\n  static tvExternalIds = (id: number, language?: string): string => {\n    let url = `${this.API_BASE_URL}tv/${id}/external_ids?api_key=${this.key}`;\n    if (language) {\n      url += `&language=${language}`;\n    }\n    return url;\n  };\n\n  static listGenre = (type: 'movie' | 'tv', language?: string): string => {\n    let url = `${this.API_BASE_URL}genre/${type}/list?api_key=${this.key}`;\n    if (language) {\n      url += `&language=${language}`;\n    }\n    return url;\n  };\n\n  static searchKeyword = (keyword: string, page?: number): string => {\n    let url = `${this.API_BASE_URL}search/${keyword}?api_key=${this.key}`;\n    if (page) {\n      url += `&page=${page}`;\n    }\n    return url;\n  };\n\n  static searchMovies = (\n    keyword: string,\n    language?: string,\n    page?: number,\n    include_adult?: boolean,\n    region?: string,\n    year?: number,\n    primary_release_year?: number,\n  ): string => {\n    let url = `${this.API_BASE_URL}search/movie?api_key=${this.key}&query=${keyword}`;\n    if (page) {\n      url += `&page=${page}`;\n    }\n    if (language) {\n      url += `&language=${language}`;\n    }\n    if (include_adult) {\n      url += `&include_adult=${include_adult}`;\n    }\n    if (region) {\n      url += `&region=${region}`;\n    }\n    if (year) {\n      url += `&year=${year}`;\n    }\n    if (primary_release_year) {\n      url += `&primary_release_year=${primary_release_year}`;\n    }\n    return url;\n  };\n\n  static searchTv = (\n    keyword: string,\n    language?: string,\n    page?: number,\n    include_adult?: boolean,\n    first_air_date_year?: number,\n  ): string => {\n    let url = `${this.API_BASE_URL}search/tv?api_key=${this.key}&query=${keyword}`;\n    if (page) {\n      url += `&page=${page}`;\n    }\n    if (language) {\n      url += `&language=${language}`;\n    }\n    if (include_adult) {\n      url += `&include_adult=${include_adult}`;\n    }\n    if (first_air_date_year) {\n      url += `&first_air_date_year=${first_air_date_year}`;\n    }\n    return url;\n  };\n\n  static searchPerson = (\n    keyword: string,\n    page?: number,\n    include_adult?: boolean,\n    language?: string,\n    region?: string,\n  ): string => {\n    let url = `${this.API_BASE_URL}search/person?api_key=${this.key}&query=${keyword}`;\n    if (page) {\n      url += `&page=${page}`;\n    }\n    if (include_adult) {\n      url += `&include_adult=${include_adult}`;\n    }\n    if (language) {\n      url += `&language=${language}`;\n    }\n    if (region) {\n      url += `&region=${region}`;\n    }\n    return url;\n  };\n\n  static listPerson = (type: ListPersonType, language?: string, page?: number): string => {\n    let url = `${this.API_BASE_URL}person/${type}?api_key=${this.key}`;\n    if (page) {\n      url += `&page=${page}`;\n    }\n    if (language) {\n      url += `&language=${language}`;\n    }\n    return url;\n  };\n\n  static personDetail = (person_id: number, language?: string): string => {\n    let url = `${this.API_BASE_URL}person/${person_id}?api_key=${this.key}`;\n    if (language) {\n      url += `&language=${language}`;\n    }\n    return url;\n  };\n\n  static peopleExternalIds = (person_id: number, language?: string): string => {\n    let url = `${this.API_BASE_URL}person/${person_id}/external_ids?api_key=${this.key}`;\n    if (language) {\n      url += `&language=${language}`;\n    }\n    return url;\n  };\n\n  static peopleImages = (person_id: number, language?: string): string => {\n    let url = `${this.API_BASE_URL}person/${person_id}/images?api_key=${this.key}`;\n    if (language) {\n      url += `&language=${language}`;\n    }\n    return url;\n  };\n\n  static peopleCredits = (\n    person_id: number,\n    type: 'movie' | 'tv' | 'combined' = 'combined',\n    language?: string,\n  ): string => {\n    let url = `${this.API_BASE_URL}person/${person_id}/${type}_credits?api_key=${this.key}`;\n    if (language) {\n      url += `&language=${language}`;\n    }\n    return url;\n  };\n\n  static discoverUrl = (\n    type: 'movie' | 'tv',\n    with_genres?: string,\n    sort_by?: string,\n    language?: string,\n    page?: number,\n    releaseDateGte?: string,\n    releaseDateLte?: string,\n    firstAirDateGte?: string,\n    firstAirDateLte?: string,\n    withOriginalLanguage?: string,\n    voteCountGte?: number,\n    voteAverageGte?: number,\n    voteAverageLte?: number,\n    withCast?: string,\n    withCrew?: string,\n    withPeople?: string,\n    withCompanies?: string,\n    withNetworks?: string,\n    withKeywords?: string,\n    withRuntimeGte?: number,\n    withRuntimeLte?: number,\n    withStatus?: string,\n    withType?: string,\n    airDateGte?: string,\n    airDateLte?: string,\n  ) => {\n    let url = `${this.API_BASE_URL}discover/${type}?api_key=${this.key}`;\n\n    if (with_genres) url += `&with_genres=${with_genres}`;\n    if (sort_by) url += `&sort_by=${sort_by}`;\n    if (language) url += `&language=${language}`;\n    if (page) url += `&page=${page}`;\n    if (withOriginalLanguage) url += `&with_original_language=${withOriginalLanguage}`;\n    if (releaseDateGte) url += `&release_date.gte=${releaseDateGte}`;\n    if (releaseDateLte) url += `&release_date.lte=${releaseDateLte}`;\n    if (firstAirDateGte) url += `&first_air_date.gte=${firstAirDateGte}`;\n    if (firstAirDateLte) url += `&first_air_date.lte=${firstAirDateLte}`;\n    if (voteCountGte) url += `&vote_count.gte=${voteCountGte}`;\n    if (voteAverageGte) url += `&vote_average.gte=${voteAverageGte}`;\n    if (voteAverageLte) url += `&vote_average.lte=${voteAverageLte}`;\n    if (withCast) url += `&with_cast=${withCast}`;\n    if (withCrew) url += `&with_crew=${withCrew}`;\n    if (withPeople) url += `&with_people=${withPeople}`;\n    if (withCompanies) url += `&with_companies=${withCompanies}`;\n    if (withNetworks) url += `&with_networks=${withNetworks}`;\n    if (withKeywords) url += `&with_keywords=${withKeywords}`;\n    if (withRuntimeGte) url += `&with_runtime.gte=${withRuntimeGte}`;\n    if (withRuntimeLte) url += `&with_runtime.lte=${withRuntimeLte}`;\n    if (withStatus) url += `&with_status=${withStatus}`;\n    if (withType) url += `&with_type=${withType}`;\n    if (airDateGte) url += `&air_date.gte=${airDateGte}`;\n    if (airDateLte) url += `&air_date.lte=${airDateLte}`;\n    return url;\n  };\n\n  static translationsUrl = (type: 'movie' | 'tv', id: number) =>\n    `${this.API_BASE_URL}${type}/${id}/translations?api_key=${this.key}`;\n\n  static languagesUrl = () => `${this.API_BASE_URL}configuration/languages?api_key=${this.key}`;\n\n  static tvSeasonDetailUrl = (tv_id: number, season_number: number, language?: string) => {\n    let url = `${this.API_BASE_URL}tv/${tv_id}/season/${season_number}?api_key=${this.key}`;\n    if (language) url += `&language=${language}`;\n    return url;\n  };\n\n  static tvSeasonCreditsUrl = (tv_id: number, season_number: number, language?: string) => {\n    let url = `${this.API_BASE_URL}tv/${tv_id}/season/${season_number}/credits?api_key=${this.key}`;\n    if (language) url += `&language=${language}`;\n    return url;\n  };\n\n  static tvSeasonImagesUrl = (tv_id: number, season_number: number, language?: string) => {\n    let url = `${this.API_BASE_URL}tv/${tv_id}/season/${season_number}/images?api_key=${this.key}`;\n    if (language) url += `&language=${language}`;\n    return url;\n  };\n\n  static tvSeasonVideosUrl = (tv_id: number, season_number: number, language?: string) => {\n    let url = `${this.API_BASE_URL}tv/${tv_id}/season/${season_number}/videos?api_key=${this.key}`;\n    if (language) url += `&language=${language}`;\n    return url;\n  };\n\n  static listUrl = (list_id: number, language?: string) => {\n    let url = `${this.API_BASE_URL}list/${list_id}?api_key=${this.key}`;\n    if (language) url += `&language=${language}`;\n    return url;\n  };\n\n  static imdbDetailUrl = (id: string) => `${this.IMDB_API_BASE_URL}title/${id}`;\n}\n\nexport const postFetchDataHandler = (\n  data: any,\n  mediaType?: 'movie' | 'tv' | 'people',\n): IMedia[] => {\n  const result: IMedia[] = [];\n\n  const transform = (item: any): IMedia =>\n    mediaType && mediaType !== 'people'\n      ? {\n          ...(mediaType && mediaType === 'movie' && { adult: item.adult }),\n          ...(mediaType && mediaType === 'movie' && { video: item.video }),\n          ...(mediaType && mediaType === 'tv' && { originalCountry: item.original_country }),\n          backdropPath: item.backdrop_path\n            ? TMDB.backdropUrl(item.backdrop_path, 'w1280')\n            : undefined,\n          genreIds: item.genre_ids,\n          id: item.id,\n          mediaType: mediaType || item.media_type,\n          originalLanguage: item.original_language,\n          originalTitle: mediaType === 'movie' ? item.original_title : item.original_name,\n          overview: item.overview,\n          popularity: item.popularity,\n          posterPath: item.poster_path ? TMDB.posterUrl(item.poster_path, 'w342') : undefined,\n          releaseDate: item.release_date || item.first_air_date,\n          title: mediaType === 'movie' ? item.title : item.name,\n          voteAverage: item.vote_average,\n          voteCount: item.vote_count,\n        }\n      : mediaType === 'people'\n      ? {\n          adult: item.adult,\n          castId: item.cast_id,\n          character: item.character,\n          creditId: item.credit_id,\n          department: item.department,\n          gender: item.gender,\n          id: item.id,\n          job: item.job,\n          knownFor: item.known_for,\n          knownForDepartment: item.known_for_department,\n          mediaType: mediaType || item.media_type,\n          title: item.name,\n          order: item.order,\n          originalName: item.original_name,\n          popularity: item.popularity,\n          posterPath: item.profile_path ? TMDB?.profileUrl(item?.profile_path, 'w185') : undefined,\n        }\n      : {\n          backdropPath: item.backdrop_path\n            ? TMDB.backdropUrl(item.backdrop_path, 'w1280')\n            : undefined,\n          genreIds: item.genre_ids,\n          id: item.id,\n          mediaType: mediaType || item.media_type,\n          originalLanguage: item.original_language,\n          originalTitle: item.original_title || item.original_name,\n          overview: item.overview,\n          popularity: item.popularity,\n          posterPath: item.poster_path ? TMDB.posterUrl(item.poster_path, 'w342') : undefined,\n          releaseDate: item.release_date || item.first_air_date,\n          title: item.title || item.name,\n          voteAverage: item.vote_average,\n          voteCount: item.vote_count,\n        };\n\n  if (Array.isArray(data?.results)) {\n    data?.results.forEach((item: any) => result.push(transform(item)));\n  } else if (Array.isArray(data)) {\n    data.forEach((item: any) => result.push(transform(item)));\n  } else {\n    result.push(transform(data));\n  }\n\n  return result;\n};\n"
  },
  {
    "path": "app/services/youtube/utils.server.ts",
    "content": "export default class Youtube {\n  static readonly API_BASE_URL = 'https://www.googleapis.com/youtube/v3/';\n\n  static readonly key = process.env.YOUTUBE_API_KEY;\n\n  static videoDetailUrl = (id: string, contentDetails?: boolean, snippet?: boolean): URL => {\n    let url = `${Youtube.API_BASE_URL}videos?id=${id}&key=${Youtube.key}`;\n    if (contentDetails) {\n      url += '&part=contentDetails';\n    }\n    if (snippet) {\n      url += '&part=snippet';\n    }\n    return new URL(url);\n  };\n}\n"
  },
  {
    "path": "app/services/youtube/youtube.server.ts",
    "content": "import Youtube from './utils.server';\nimport type { IYoutubeItem, IYoutubeVideoList } from './youtube.types';\n\nconst fetcher = async <T = any>(url: URL): Promise<T> => {\n  const res = await fetch(url);\n  return res.json();\n};\n\nexport const getYoutubeVideo = async (\n  id: string,\n  contentDetails?: boolean,\n  snippet?: boolean,\n): Promise<IYoutubeItem[] | undefined> => {\n  try {\n    const fetched = await fetcher<IYoutubeVideoList>(\n      Youtube.videoDetailUrl(id, contentDetails, snippet),\n    );\n    return fetched.items;\n  } catch (error) {\n    console.error(error);\n  }\n};\n"
  },
  {
    "path": "app/services/youtube/youtube.types.ts",
    "content": "export interface IYoutubeVideoList {\n  etag: string;\n  items: IYoutubeItem[];\n  kind: string;\n  pageInfo: PageInfo;\n}\n\nexport interface IYoutubeItem {\n  contentDetails: ContentDetails;\n  etag: string;\n  id: string;\n  kind: string;\n  snippet: Snippet;\n}\n\nexport interface ContentDetails {\n  caption: string;\n  contentRating: ContentRating;\n  definition: string;\n  dimension: string;\n  duration: string;\n  licensedContent: boolean;\n  projection: string;\n}\n\nexport interface ContentRating {\n  acbRating: string;\n  agcomRating: string;\n  anatelRating: string;\n  bbfcRating: string;\n  bfvcRating: string;\n  bmukkRating: string;\n  catvRating: string;\n  catvfrRating: string;\n  cbfcRating: string;\n  cccRating: string;\n  cceRating: string;\n  chfilmRating: string;\n  chvrsRating: string;\n  cicfRating: string;\n  cnaRating: string;\n  cncRating: string;\n  csaRating: string;\n  cscfRating: string;\n  czfilmRating: string;\n  djctqRating: string;\n  djctqRatingReasons: string[];\n  ecbmctRating: string;\n  eefilmRating: string;\n  egfilmRating: string;\n  eirinRating: string;\n  fcbmRating: string;\n  fcoRating: string;\n  fmocRating: string;\n  fpbRating: string;\n  fpbRatingReasons: string[];\n  fskRating: string;\n  grfilmRating: string;\n  icaaRating: string;\n  ifcoRating: string;\n  ilfilmRating: string;\n  incaaRating: string;\n  kfcbRating: string;\n  kijkwijzerRating: string;\n  kmrbRating: string;\n  lsfRating: string;\n  mccaaRating: string;\n  mccypRating: string;\n  mcstRating: string;\n  mdaRating: string;\n  medietilsynetRating: string;\n  mekuRating: string;\n  mibacRating: string;\n  mocRating: string;\n  moctwRating: string;\n  mpaaRating: string;\n  mpaatRating: string;\n  mtrcbRating: string;\n  nbcRating: string;\n  nbcplRating: string;\n  nfrcRating: string;\n  nfvcbRating: string;\n  nkclvRating: string;\n  oflcRating: string;\n  pefilmRating: string;\n  rcnofRating: string;\n  resorteviolenciaRating: string;\n  rtcRating: string;\n  rteRating: string;\n  russiaRating: string;\n  skfilmRating: string;\n  smaisRating: string;\n  smsaRating: string;\n  tvpgRating: string;\n  ytRating: string;\n}\n\nexport interface Snippet {\n  categoryId: string;\n  channelId: string;\n  channelTitle: string;\n  defaultAudioLanguage: string;\n  description: string;\n  liveBroadcastContent: string;\n  localized: Localized;\n  publishedAt: Date;\n  tags: string[];\n  thumbnails: Thumbnails;\n  title: string;\n}\n\nexport interface Localized {\n  description: string;\n  title: string;\n}\n\nexport interface Thumbnails {\n  default: Default;\n  high: Default;\n  maxres: Default;\n  medium: Default;\n  standard: Default;\n}\n\nexport interface Default {\n  height: number;\n  url: string;\n  width: number;\n}\n\nexport interface PageInfo {\n  resultsPerPage: number;\n  totalResults: number;\n}\n"
  },
  {
    "path": "app/store/card/useCardHoverStore.ts",
    "content": "import { create } from 'zustand';\n\ninterface CardHoverState {\n  isCardPlaying: boolean;\n  toggleCardPlaying: () => void;\n  setIsCardPlaying: (isCardPlaying: boolean) => void;\n}\n\nconst defaultState = {\n  isCardPlaying: false,\n};\n\nconst useCardHoverStore = create<CardHoverState>((set) => ({\n  ...defaultState,\n  toggleCardPlaying: () => set((state) => ({ isCardPlaying: !state.isCardPlaying })),\n  setIsCardPlaying: (isCardPlaying: boolean) => set({ isCardPlaying }),\n}));\n\nexport default useCardHoverStore;\n"
  },
  {
    "path": "app/store/layout/useHeaderStyle.ts",
    "content": "import { create } from 'zustand';\n\nexport interface HeaderStyleState {\n  backgroundColor: string;\n  setBackgroundColor: (backgroundColor: string) => void;\n  startChangeScrollPosition: number;\n  setStartChangeScrollPosition: (startChangeScrollPosition: number) => void;\n}\n\nconst defaultState = {\n  backgroundColor: '',\n  startChangeScrollPosition: 0,\n};\n\nexport const useHeaderStyle = create<HeaderStyleState>((set) => ({\n  ...defaultState,\n  setBackgroundColor: (backgroundColor: string) => set({ backgroundColor }),\n  setStartChangeScrollPosition: (startChangeScrollPosition: number) =>\n    set({ startChangeScrollPosition }),\n}));\n"
  },
  {
    "path": "app/store/layout/useHistoryStack.ts",
    "content": "import { create } from 'zustand';\n\nexport interface HistoryStackState {\n  historyBack: string[];\n  historyForward: string[];\n  setHistoryBack: (historyBack: string[]) => void;\n  setHistoryForward: (historyForward: string[]) => void;\n}\n\nconst defaultState = {\n  historyBack: [],\n  historyForward: [],\n};\n\nexport const useHistoryStack = create<HistoryStackState>((set) => ({\n  ...defaultState,\n  setHistoryBack: (historyBack: string[]) => set({ historyBack }),\n  setHistoryForward: (historyForward: string[]) => set({ historyForward }),\n}));\n"
  },
  {
    "path": "app/store/layout/useLayout.ts",
    "content": "import { MotionValue } from 'framer-motion';\nimport { create } from 'zustand';\n\nexport interface LayoutState {\n  viewportRef: React.RefObject<HTMLDivElement>;\n  setViewportRef: (viewportRef: React.RefObject<HTMLDivElement>) => void;\n  scrollY: MotionValue<number>;\n  setScrollY: (scrollY: MotionValue<number>) => void;\n  scrollYProgress: MotionValue<number>;\n  setScrollYProgress: (scrollYProgress: MotionValue<number>) => void;\n  scrollDirection: 'down' | 'up' | undefined;\n  setScrollDirection: (scrollDirection: 'down' | 'up' | undefined) => void;\n  isShowOverlay: boolean;\n  setIsShowOverlay: (isShowOverlay: boolean) => void;\n}\n\nconst defaultState = {\n  viewportRef: { current: null },\n  scrollY: new MotionValue(),\n  scrollYProgress: new MotionValue(),\n  scrollDirection: undefined,\n  isShowOverlay: false,\n};\n\nexport const useLayout = create<LayoutState>((set) => ({\n  ...defaultState,\n  setScrollDirection: (scrollDirection: 'down' | 'up' | undefined) => set({ scrollDirection }),\n  setViewportRef: (viewportRef: React.RefObject<HTMLDivElement>) => set({ viewportRef }),\n  setScrollY: (scrollY: MotionValue<number>) => set({ scrollY }),\n  setScrollYProgress: (scrollYProgress: MotionValue<number>) => set({ scrollYProgress }),\n  setIsShowOverlay: (isShowOverlay: boolean) => set({ isShowOverlay }),\n}));\n"
  },
  {
    "path": "app/store/player/usePlayerState.ts",
    "content": "/* eslint-disable @typescript-eslint/ban-ts-comment */\n/* eslint-disable @typescript-eslint/indent */\nimport { create } from 'zustand';\n\nimport type { ITrailer } from '~/services/consumet/anilist/anilist.types';\nimport type { IMovieSource, IMovieSubtitle } from '~/services/tmdb/tmdb.types';\n\nexport type PlayerData =\n  | {\n      provider?: string;\n      idProvider?: number | string;\n      sources: IMovieSource[] | undefined;\n      subtitles?: IMovieSubtitle[] | undefined;\n      hasNextEpisode?: boolean;\n      routePlayer: string;\n      titlePlayer: string;\n      id: number | string;\n      posterPlayer: string;\n      typeVideo: 'movie' | 'tv' | 'anime';\n      trailerAnime?: ITrailer;\n      currentEpisode?: number;\n      userId?: string;\n      subtitleOptions?: {\n        imdb_id?: number;\n        tmdb_id?: number;\n        parent_feature_id?: number;\n        parent_imdb_id?: number;\n        parent_tmdb_id?: number;\n        episode_number?: number;\n        season_number?: number;\n        type?: 'movie' | 'episode' | 'all';\n        title?: string;\n        sub_format: 'srt' | 'webvtt';\n      };\n      overview: string;\n      malId?: number;\n      highlights?: {\n        start: number;\n        end: number;\n        text: string;\n      }[];\n    }\n  | undefined;\n\nexport type QualitySelector = {\n  html: string;\n  url: string;\n  default?: boolean;\n}[];\n\nexport type SubtitleSelector =\n  | {\n      html: string;\n      url: string;\n      type?: string;\n      default?: true;\n    }[]\n  | undefined;\n\ninterface PlayerState {\n  shouldShowPlayer: boolean;\n  toggleShowPlayer: () => void;\n  setShouldShowPlayer: (shouldShowPlayer: boolean) => void;\n  isMini: boolean;\n  toggleMini: () => void;\n  setIsMini: (isMini: boolean) => void;\n  routePlayer: string;\n  setRoutePlayer: (routePlayer: string) => void;\n  titlePlayer: string;\n  setTitlePlayer: (titlePlayer: string) => void;\n  playerData: PlayerData;\n  setPlayerData: (playerData: PlayerData) => void;\n  qualitySelector: QualitySelector;\n  setQualitySelector: (qualitySelector: QualitySelector) => void;\n  subtitleSelector: SubtitleSelector;\n  setSubtitleSelector: (subtitleSelector: SubtitleSelector) => void;\n  updateSubtitleSelector: (subtitles: SubtitleSelector) => void;\n}\n\nconst defaultState = {\n  shouldShowPlayer: false,\n  isMini: false,\n  routePlayer: '',\n  titlePlayer: '',\n  playerData: undefined,\n  qualitySelector: [],\n  subtitleSelector: [],\n};\n\nconst usePlayerState = create<PlayerState>((set) => ({\n  ...defaultState,\n  toggleShowPlayer: () => set((state) => ({ shouldShowPlayer: !state.shouldShowPlayer })),\n  setShouldShowPlayer: (shouldShowPlayer: boolean) => set({ shouldShowPlayer }),\n  toggleMini: () => set((state) => ({ isMini: !state.isMini })),\n  setIsMini: (isMini: boolean) => set({ isMini }),\n  setRoutePlayer: (routePlayer: string) => set({ routePlayer }),\n  setTitlePlayer: (titlePlayer: string) => set({ titlePlayer }),\n  setPlayerData: (playerData: PlayerData) => set({ playerData }),\n  setQualitySelector: (qualitySelector: QualitySelector) => set({ qualitySelector }),\n  updateSubtitleSelector: (subtitles: SubtitleSelector) =>\n    // @ts-ignore\n    set((state) => ({ subtitleSelector: [...state.subtitleSelector, ...subtitles] })),\n  setSubtitleSelector: (subtitleSelector: SubtitleSelector) => set({ subtitleSelector }),\n}));\n\nexport default usePlayerState;\n"
  },
  {
    "path": "app/styles/tailwind.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n@tailwind variants;\n\n@layer base {\n  * {\n    @apply box-border m-0 p-0;\n  }\n\n  body {\n    @apply !bg-background-body;\n  }\n\n  :root {\n    --swiper-theme-color: hsl(var(--theme-primary)) !important;\n    --swiper-pagination-bullet-inactive-color: hsl(var(--theme-primary-700)) !important;\n    --theme-rounded-base: 0.25rem;\n  }\n\n  .h0 {\n    @apply text-5xl font-bold md:text-6xl !tracking-tighter text-foreground;\n  }\n\n  h1,\n  .h1 {\n    @apply text-4xl font-bold md:text-5xl !tracking-tighter text-foreground;\n  }\n\n  h2,\n  .h2 {\n    @apply text-3xl font-bold md:text-4xl !tracking-tighter text-foreground;\n  }\n\n  h3,\n  .h3 {\n    @apply text-2xl font-semibold md:text-3xl !tracking-normal text-foreground;\n  }\n\n  h4,\n  .h4 {\n    @apply text-xl font-semibold md:text-2xl !tracking-tight text-foreground;\n  }\n\n  h5,\n  .h5 {\n    @apply text-lg font-medium md:text-xl !tracking-tight text-foreground;\n  }\n\n  h6,\n  .h6 {\n    @apply text-base font-medium md:text-lg !tracking-tight text-foreground;\n  }\n\n  body,\n  p,\n  .p {\n    @apply text-base !tracking-normal text-foreground;\n  }\n\n  .text-display {\n    @apply text-inherit text-base !tracking-normal font-normal md:text-lg text-foreground;\n  }\n\n  .text-gradient {\n    @apply bg-gradient-to-r from-primary via-default-600 to-secondary bg-clip-text text-transparent;\n  }\n\n  .cyberpunk {\n    @apply font-mono;\n  }\n\n  .wireframe {\n    font-family: Chalkboard, comic sans ms, sanssecondaryerif;\n  }\n}\n\n@layer components {\n  .custom-player-subtitle {\n    .art-subtitle {\n      p {\n        @apply text-player-subtitle-color bg-player-subtitle-background-color p-1 m-0 !text-player-subtitle-font-size !leading-normal;\n      }\n    }\n  }\n\n  .custom-player-layer-auto-playback {\n    .art-video-player {\n      .art-layers {\n        .art-layer-auto-playback {\n          @apply !rounded-medium !border !border-solid !border-divider !shadow-lg !backdrop-blur-2xl !backdrop-contrast-125 !backdrop-saturate-200 !bg-default/[0.6];\n        }\n      }\n    }\n  }\n\n  .custom-player-contextmenus {\n    .art-video-player.art-contextmenu-show {\n      .art-contextmenus {\n        @apply !rounded-medium !border !border-solid !border-divider !shadow-lg !backdrop-blur-2xl !backdrop-contrast-125 !backdrop-saturate-200 !bg-default/[0.6];\n      }\n    }\n  }\n\n  .custom-player-info {\n    .art-video-player.art-info-show {\n      .art-info {\n        @apply !rounded-medium !border !border-solid !border-divider !shadow-lg !backdrop-blur-2xl !backdrop-contrast-125 !backdrop-saturate-200 !bg-default/[0.6];\n      }\n    }\n  }\n\n  .custom-player-notice-inner {\n    .art-notice-inner {\n      @apply !rounded-medium !border !border-solid !border-divider !shadow-lg !backdrop-blur-2xl !backdrop-contrast-125 !backdrop-saturate-200 !bg-default/[0.6];\n    }\n  }\n\n  .custom-mini-player-hover {\n    .art-video-player:hover {\n      @apply [&_.art-layer-mask]:block [&_.art-layer-mask]:!bg-default/[0.6] [&_.art-layer-playPauseButton]:block [&_.art-layer-miniTopControlButtons]:block;\n    }\n  }\n\n  .custom-player-volume-control {\n    .art-video-player {\n      .art-volume-panel {\n        .art-volume-inner {\n          @apply !rounded-medium !border !border-solid !border-divider !shadow-lg !backdrop-blur-2xl !backdrop-contrast-125 !backdrop-saturate-200 !bg-default/[0.6];\n        }\n      }\n    }\n  }\n\n  .custom-player-icon-after {\n    .art-icon::after {\n      @apply !rounded-medium !border !border-solid !border-divider !shadow-lg !backdrop-blur-2xl !backdrop-contrast-125 !backdrop-saturate-200 !text-default-foreground !font-sans !bg-default/[0.6];\n    }\n  }\n\n  .custom-player-icon-before {\n    .art-icon::before {\n      @apply !border-t-background/[0.6];\n    }\n  }\n\n  .custom-player-control-after {\n    .art-control::after {\n      @apply !rounded-medium !border !border-solid !border-divider !shadow-lg !backdrop-blur-2xl !backdrop-contrast-125 !backdrop-saturate-200 !text-default-foreground !font-sans !bg-default/[0.6];\n    }\n  }\n\n  .custom-player-control-before {\n    .art-control::before {\n      @apply !border-t-background/[0.6];\n    }\n  }\n\n  .custom-player-layer-lock {\n    .art-video-player {\n      .art-layers {\n        .art-layer-lock {\n          @apply !border !border-solid !border-divider !shadow-lg !backdrop-blur-2xl !backdrop-contrast-125 !backdrop-saturate-200 !bg-default/[0.6];\n        }\n      }\n    }\n  }\n\n  .custom-player-progress-tip {\n    .art-video-player {\n      .art-bottom\n        .art-progress\n        .art-control-progress\n        .art-control-progress-inner\n        .art-progress-tip {\n        @apply !rounded-medium !border !border-solid !border-divider !shadow-lg !backdrop-blur-2xl !backdrop-contrast-125 !backdrop-saturate-200 !bg-default/[0.6];\n      }\n    }\n  }\n\n  .lottie-color {\n    svg {\n      path {\n        @apply stroke-default-foreground;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "app/styles/themes.config.ts",
    "content": "// custom themes is taken and customized from daisy ui themes\n// https://github.com/saadeghi/daisyui/blob/master/src/theming/themes.js\n\nimport { commonColors, semanticColors } from '@nextui-org/theme';\n\nimport { swapColorValues } from '../utils';\n\nconst lightBase = {\n  colors: {\n    'background-body': '#ffffff',\n    'background-title-bar': '#fefefe',\n  },\n};\n\nconst darkBase = {\n  colors: {\n    'background-body': '#000000',\n    'background-title-bar': '#070708',\n  },\n};\n\nconst lightRedTheme = {\n  extend: 'light',\n  colors: {\n    ...semanticColors.light,\n    ...lightBase.colors,\n    primary: {\n      ...commonColors.red,\n      foreground: commonColors.white,\n      DEFAULT: commonColors.red[500],\n    },\n    secondary: {\n      ...commonColors.yellow,\n      foreground: commonColors.white,\n      DEFAULT: commonColors.yellow[500],\n    },\n    focus: {\n      DEFAULT: commonColors.red[500],\n    },\n  },\n};\n\nconst lightYellowTheme = {\n  extend: 'light',\n  colors: {\n    ...semanticColors.light,\n    ...lightBase.colors,\n    primary: {\n      ...commonColors.yellow,\n      foreground: commonColors.white,\n      DEFAULT: commonColors.yellow[500],\n    },\n    secondary: {\n      ...commonColors.green,\n      foreground: commonColors.white,\n      DEFAULT: commonColors.green[500],\n    },\n    focus: {\n      DEFAULT: commonColors.yellow[500],\n    },\n  },\n};\n\nconst lightGreenTheme = {\n  extend: 'light',\n  colors: {\n    ...semanticColors.light,\n    ...lightBase.colors,\n    primary: {\n      ...commonColors.green,\n      foreground: commonColors.white,\n      DEFAULT: commonColors.green[500],\n    },\n    secondary: {\n      ...commonColors.cyan,\n      foreground: commonColors.white,\n      DEFAULT: commonColors.cyan[500],\n    },\n    focus: {\n      DEFAULT: commonColors.green[500],\n    },\n  },\n};\n\nconst lightCyanTheme = {\n  extend: 'light',\n  colors: {\n    ...semanticColors.light,\n    ...lightBase.colors,\n    primary: {\n      ...commonColors.cyan,\n      foreground: commonColors.white,\n      DEFAULT: commonColors.cyan[500],\n    },\n    secondary: {\n      ...commonColors.blue,\n      foreground: commonColors.white,\n      DEFAULT: commonColors.blue[500],\n    },\n    focus: {\n      DEFAULT: commonColors.cyan[500],\n    },\n  },\n};\n\nconst lightPurpleTheme = {\n  extend: 'light',\n  colors: {\n    ...semanticColors.light,\n    ...lightBase.colors,\n    primary: {\n      ...commonColors.purple,\n      foreground: commonColors.white,\n      DEFAULT: commonColors.purple[500],\n    },\n    secondary: {\n      ...commonColors.pink,\n      foreground: commonColors.white,\n      DEFAULT: commonColors.pink[500],\n    },\n    focus: {\n      DEFAULT: commonColors.purple[500],\n    },\n  },\n};\n\nconst lightPinkTheme = {\n  extend: 'light',\n  colors: {\n    ...semanticColors.light,\n    ...lightBase.colors,\n    primary: {\n      ...commonColors.pink,\n      foreground: commonColors.white,\n      DEFAULT: commonColors.pink[500],\n    },\n    secondary: {\n      ...commonColors.red,\n      foreground: commonColors.white,\n      DEFAULT: commonColors.red[500],\n    },\n    focus: {\n      DEFAULT: commonColors.pink[500],\n    },\n  },\n};\n\nconst darkRedTheme = {\n  extend: 'dark',\n  colors: {\n    ...semanticColors.dark,\n    ...darkBase.colors,\n    primary: {\n      ...swapColorValues(commonColors.red),\n      foreground: commonColors.white,\n      DEFAULT: commonColors.red[500],\n    },\n    secondary: {\n      ...swapColorValues(commonColors.yellow),\n      foreground: commonColors.white,\n      DEFAULT: commonColors.yellow[500],\n    },\n    focus: {\n      DEFAULT: commonColors.red[500],\n    },\n  },\n};\n\nconst darkYellowTheme = {\n  extend: 'dark',\n  colors: {\n    ...semanticColors.dark,\n    ...darkBase.colors,\n    primary: {\n      ...swapColorValues(commonColors.yellow),\n      foreground: commonColors.white,\n      DEFAULT: commonColors.yellow[500],\n    },\n    secondary: {\n      ...swapColorValues(commonColors.green),\n      foreground: commonColors.white,\n      DEFAULT: commonColors.green[500],\n    },\n    focus: {\n      DEFAULT: commonColors.yellow[500],\n    },\n  },\n};\n\nconst darkGreenTheme = {\n  extend: 'dark',\n  colors: {\n    ...semanticColors.dark,\n    ...darkBase.colors,\n    primary: {\n      ...swapColorValues(commonColors.green),\n      foreground: commonColors.white,\n      DEFAULT: commonColors.green[500],\n    },\n    secondary: {\n      ...swapColorValues(commonColors.cyan),\n      foreground: commonColors.white,\n      DEFAULT: commonColors.cyan[500],\n    },\n    focus: {\n      DEFAULT: commonColors.green[500],\n    },\n  },\n};\n\nconst darkCyanTheme = {\n  extend: 'dark',\n  colors: {\n    ...semanticColors.dark,\n    ...darkBase.colors,\n    primary: {\n      ...swapColorValues(commonColors.cyan),\n      foreground: commonColors.white,\n      DEFAULT: commonColors.cyan[500],\n    },\n    secondary: {\n      ...swapColorValues(commonColors.blue),\n      foreground: commonColors.white,\n      DEFAULT: commonColors.blue[500],\n    },\n    focus: {\n      DEFAULT: commonColors.cyan[500],\n    },\n  },\n};\n\nconst darkPurpleTheme = {\n  extend: 'dark',\n  colors: {\n    ...semanticColors.dark,\n    ...darkBase.colors,\n    primary: {\n      ...swapColorValues(commonColors.purple),\n      foreground: commonColors.white,\n      DEFAULT: commonColors.purple[500],\n    },\n    secondary: {\n      ...swapColorValues(commonColors.pink),\n      foreground: commonColors.white,\n      DEFAULT: commonColors.pink[500],\n    },\n    focus: {\n      DEFAULT: commonColors.purple[500],\n    },\n  },\n};\n\nconst darkPinkTheme = {\n  extend: 'dark',\n  colors: {\n    ...semanticColors.dark,\n    ...darkBase.colors,\n    primary: {\n      ...swapColorValues(commonColors.pink),\n      foreground: commonColors.white,\n      DEFAULT: commonColors.pink[500],\n    },\n    secondary: {\n      ...swapColorValues(commonColors.red),\n      foreground: commonColors.white,\n      DEFAULT: commonColors.red[500],\n    },\n    focus: {\n      DEFAULT: commonColors.pink[500],\n    },\n  },\n};\n\nconst cupcakeTheme = {\n  extend: 'light',\n  colors: {\n    'background-title-bar': '#f9f6fc',\n    'background-body': '#ffffff',\n    background: {\n      DEFAULT: '#faf7f5',\n    },\n    foreground: {\n      DEFAULT: '#281435',\n    },\n    divider: {\n      DEFAULT: 'rgba(40, 20, 53, 0.25)',\n    },\n    focus: {\n      DEFAULT: '#5fb8be',\n    },\n    content1: {\n      DEFAULT: '#EADEF2',\n      foreground: '#08040B',\n    },\n    content2: {\n      DEFAULT: '#D8C0E7',\n      foreground: '#110816',\n    },\n    content3: {\n      DEFAULT: '#B081CF',\n      foreground: '#190D21',\n    },\n    content4: {\n      DEFAULT: '#8844B5',\n      foreground: '#1F0F29',\n    },\n    default: {\n      50: '#EADEF2',\n      100: '#D8C0E7',\n      200: '#B081CF',\n      300: '#8844B5',\n      400: '#562B73',\n      500: '#281435',\n      600: '#1F0F29',\n      700: '#190D21',\n      800: '#110816',\n      900: '#08040B',\n      DEFAULT: '#8844B5',\n      foreground: '#110816',\n    },\n    primary: {\n      50: '#F0F9F9',\n      100: '#E2F2F3',\n      200: '#C8E7E9',\n      300: '#ABDADE',\n      400: '#91CFD4',\n      500: '#74C2C8',\n      600: '#48AFB7',\n      700: '#358287',\n      800: '#24585B',\n      900: '#112A2C',\n      DEFAULT: '#74C2C8',\n      foreground: '#1c2728',\n    },\n    secondary: {\n      50: '#FDF7F9',\n      100: '#FAEAF1',\n      200: '#F6DAE5',\n      300: '#F1C6D7',\n      400: '#EDB5CC',\n      500: '#E8A0BD',\n      600: '#D96392',\n      700: '#BD2E67',\n      800: '#7F1F45',\n      900: '#3E0F22',\n      DEFAULT: '#E8A0BD',\n      foreground: '#2e2327',\n    },\n    success: {\n      50: '#EEFEEC',\n      100: '#DFFCE3',\n      200: '#C0FACD',\n      300: '#9EF1B8',\n      400: '#81E3AA',\n      500: '#59D298',\n      600: '#41B488',\n      700: '#2C9779',\n      800: '#1C7969',\n      900: '#11645E',\n      DEFAULT: '#59D298',\n      foreground: '#0b3320',\n    },\n    warning: {\n      50: '#FEFBE2',\n      100: '#FEF7CF',\n      200: '#FDEEA1',\n      300: '#FBE171',\n      400: '#F8D34E',\n      500: '#F4BE15',\n      600: '#D19D0F',\n      700: '#AF7F0A',\n      800: '#8D6206',\n      900: '#754E04',\n      DEFAULT: '#F4BE15',\n      foreground: '#362800',\n    },\n    danger: {\n      50: '#FEF6EE',\n      100: '#FEEDE3',\n      200: '#FDD6C8',\n      300: '#F9B9AB',\n      400: '#F49E95',\n      500: '#EE7372',\n      600: '#CC535C',\n      700: '#AB394C',\n      800: '#8A243D',\n      900: '#721534',\n      DEFAULT: '#EE7372',\n      foreground: '#430100',\n    },\n  },\n  layout: {\n    radius: {\n      small: '0.75rem',\n      medium: '1rem',\n      large: '1.5rem',\n    },\n  },\n};\n\nconst bumblebeeTheme = {\n  extend: 'light',\n  colors: {\n    'background-title-bar': '#f9f9fe',\n    'background-body': '#ffffff',\n    background: {\n      DEFAULT: '#ffffff',\n    },\n    foreground: {\n      DEFAULT: '#333333',\n    },\n    divider: {\n      DEFAULT: 'rgba(51, 51, 51, 0.25)',\n    },\n    focus: {\n      DEFAULT: '#f3d000',\n    },\n    content1: {\n      DEFAULT: '#ECECFB',\n      foreground: '#040417',\n    },\n    content2: {\n      DEFAULT: '#DCDCF4',\n      foreground: '#07071B',\n    },\n    content3: {\n      DEFAULT: '#BBBBEA',\n      foreground: '#0C0C22',\n    },\n    content4: {\n      DEFAULT: '#8787C0',\n      foreground: '#111129',\n    },\n    default: {\n      50: '#ECECFB',\n      100: '#DCDCF4',\n      200: '#BBBBEA',\n      300: '#8787C0',\n      400: '#515182',\n      500: '#181830',\n      600: '#111129',\n      700: '#0C0C22',\n      800: '#07071B',\n      900: '#040417',\n      DEFAULT: '#8787C0',\n      foreground: '#07071B',\n    },\n    primary: {\n      50: '#FEF9E4',\n      100: '#FBF3D2',\n      200: '#F8E4A6',\n      300: '#ECCB76',\n      400: '#D9AF52',\n      500: '#C08921',\n      600: '#A56F18',\n      700: '#8A5710',\n      800: '#6F410A',\n      900: '#5C3206',\n      DEFAULT: '#C08921',\n      foreground: '#181830',\n    },\n    secondary: {\n      50: '#FDFBE5',\n      100: '#F8F1D3',\n      200: '#F0E2A3',\n      300: '#E9D477',\n      400: '#E2C74B',\n      500: '#D6B422',\n      600: '#AC911B',\n      700: '#806C14',\n      800: '#54470D',\n      900: '#2C2507',\n      DEFAULT: '#D6B422',\n      foreground: '#181830',\n    },\n    success: {\n      50: '#EEFEEC',\n      100: '#DFFCE3',\n      200: '#C0FACD',\n      300: '#9EF1B8',\n      400: '#81E3AA',\n      500: '#59D298',\n      600: '#41B488',\n      700: '#2C9779',\n      800: '#1C7969',\n      900: '#11645E',\n      DEFAULT: '#59D298',\n      foreground: '#0b3320',\n    },\n    warning: {\n      50: '#FEFBE2',\n      100: '#FEF7CF',\n      200: '#FDEEA1',\n      300: '#FBE171',\n      400: '#F8D34E',\n      500: '#F4BE15',\n      600: '#D19D0F',\n      700: '#AF7F0A',\n      800: '#8D6206',\n      900: '#754E04',\n      DEFAULT: '#F4BE15',\n      foreground: '#362800',\n    },\n    danger: {\n      50: '#FEF6EE',\n      100: '#FEEDE3',\n      200: '#FDD6C8',\n      300: '#F9B9AB',\n      400: '#F49E95',\n      500: '#EE7372',\n      600: '#CC535C',\n      700: '#AB394C',\n      800: '#8A243D',\n      900: '#721534',\n      DEFAULT: '#EE7372',\n      foreground: '#430100',\n    },\n  },\n};\n\nconst emeraldTheme = {\n  extend: 'light',\n  colors: {\n    'background-title-bar': '#f9fafb',\n    'background-body': '#ffffff',\n    background: {\n      DEFAULT: '#ffffff',\n    },\n    foreground: {\n      DEFAULT: '#343c4d',\n    },\n    divider: {\n      DEFAULT: 'rgba(52, 60, 77, 0.25)',\n    },\n    focus: {\n      DEFAULT: '#60c273',\n    },\n    content1: {\n      DEFAULT: '#EAECF1',\n      foreground: '#0A0C0F',\n    },\n    content2: {\n      DEFAULT: '#D1D6E0',\n      foreground: '#15181E',\n    },\n    content3: {\n      DEFAULT: '#A4ADC1',\n      foreground: '#1F242E',\n    },\n    content4: {\n      DEFAULT: '#7684A3',\n      foreground: '#292F3D',\n    },\n    default: {\n      50: '#EAECF1',\n      100: '#D1D6E0',\n      200: '#A4ADC1',\n      300: '#7684A3',\n      400: '#525F7A',\n      500: '#343C4D',\n      600: '#292F3D',\n      700: '#1F242E',\n      800: '#15181E',\n      900: '#0A0C0F',\n      DEFAULT: '#7684A3',\n      foreground: '#15181E',\n    },\n    primary: {\n      50: '#F0F9F2',\n      100: '#E5F5E9',\n      200: '#C7EACF',\n      300: '#ADE0B8',\n      400: '#90D59F',\n      500: '#75CB88',\n      600: '#46B95F',\n      700: '#358D48',\n      800: '#235D2F',\n      900: '#123019',\n      DEFAULT: '#75CB88',\n      foreground: '#273e31',\n    },\n    secondary: {\n      50: '#EBF0FF',\n      100: '#DCE6FF',\n      200: '#B8CCFE',\n      300: '#90AFFE',\n      400: '#6D96FD',\n      500: '#497CFD',\n      600: '#084DFC',\n      700: '#0238C0',\n      800: '#012683',\n      900: '#011342',\n      DEFAULT: '#497CFD',\n      foreground: '#f9fafb',\n    },\n    success: {\n      50: '#EEFEEC',\n      100: '#DFFCE3',\n      200: '#C0FACD',\n      300: '#9EF1B8',\n      400: '#81E3AA',\n      500: '#59D298',\n      600: '#41B488',\n      700: '#2C9779',\n      800: '#1C7969',\n      900: '#11645E',\n      DEFAULT: '#59D298',\n      foreground: '#0b3320',\n    },\n    warning: {\n      50: '#FEFBE2',\n      100: '#FEF7CF',\n      200: '#FDEEA1',\n      300: '#FBE171',\n      400: '#F8D34E',\n      500: '#F4BE15',\n      600: '#D19D0F',\n      700: '#AF7F0A',\n      800: '#8D6206',\n      900: '#754E04',\n      DEFAULT: '#F4BE15',\n      foreground: '#362800',\n    },\n    danger: {\n      50: '#FEF6EE',\n      100: '#FEEDE3',\n      200: '#FDD6C8',\n      300: '#F9B9AB',\n      400: '#F49E95',\n      500: '#EE7372',\n      600: '#CC535C',\n      700: '#AB394C',\n      800: '#8A243D',\n      900: '#721534',\n      DEFAULT: '#EE7372',\n      foreground: '#430100',\n    },\n  },\n};\n\nconst corporateTheme = {\n  extend: 'light',\n  colors: {\n    'background-title-bar': '#f7f7fa',\n    'background-body': '#ffffff',\n    background: {\n      DEFAULT: '#ffffff',\n    },\n    foreground: {\n      DEFAULT: '#191a2a',\n    },\n    divider: {\n      DEFAULT: 'rgba(25, 26, 42, 0.25)',\n    },\n    focus: {\n      DEFAULT: '#3850fd',\n    },\n    content1: {\n      DEFAULT: '#E2E3EE',\n      foreground: '#06060A',\n    },\n    content2: {\n      DEFAULT: '#C9CADF',\n      foreground: '#0A0A10',\n    },\n    content3: {\n      DEFAULT: '#8F92BC',\n      foreground: '#0F101A',\n    },\n    content4: {\n      DEFAULT: '#5B5F99',\n      foreground: '#131420',\n    },\n    default: {\n      50: '#E2E3EE',\n      100: '#C9CADF',\n      200: '#8F92BC',\n      300: '#5B5F99',\n      400: '#393C60',\n      500: '#191A2A',\n      600: '#131420',\n      700: '#0F101A',\n      800: '#0A0A10',\n      900: '#06060A',\n      DEFAULT: '#7684A3',\n      foreground: '#0A0A10',\n    },\n    primary: {\n      50: '#F0F2FF',\n      100: '#DCE1FF',\n      200: '#B8C3FE',\n      300: '#9AA9FE',\n      400: '#778BFD',\n      500: '#546CFD',\n      600: '#1235FC',\n      700: '#0220CA',\n      800: '#021583',\n      900: '#010A42',\n      DEFAULT: '#546CFD',\n      foreground: '#e2e0ff',\n    },\n    secondary: {\n      50: '#F2F4F7',\n      100: '#E6E9F0',\n      200: '#CCD3E0',\n      300: '#B3BED1',\n      400: '#99A8C2',\n      500: '#7F92B2',\n      600: '#5C7299',\n      700: '#455673',\n      800: '#2E394C',\n      900: '#171D26',\n      DEFAULT: '#7F92B2',\n      foreground: '#1d2026',\n    },\n    success: {\n      50: '#EEFEEC',\n      100: '#DFFCE3',\n      200: '#C0FACD',\n      300: '#9EF1B8',\n      400: '#81E3AA',\n      500: '#59D298',\n      600: '#41B488',\n      700: '#2C9779',\n      800: '#1C7969',\n      900: '#11645E',\n      DEFAULT: '#59D298',\n      foreground: '#0b3320',\n    },\n    warning: {\n      50: '#FEFBE2',\n      100: '#FEF7CF',\n      200: '#FDEEA1',\n      300: '#FBE171',\n      400: '#F8D34E',\n      500: '#F4BE15',\n      600: '#D19D0F',\n      700: '#AF7F0A',\n      800: '#8D6206',\n      900: '#754E04',\n      DEFAULT: '#F4BE15',\n      foreground: '#362800',\n    },\n    danger: {\n      50: '#FEF6EE',\n      100: '#FEEDE3',\n      200: '#FDD6C8',\n      300: '#F9B9AB',\n      400: '#F49E95',\n      500: '#EE7372',\n      600: '#CC535C',\n      700: '#AB394C',\n      800: '#8A243D',\n      900: '#721534',\n      DEFAULT: '#EE7372',\n      foreground: '#430100',\n    },\n  },\n  layout: {\n    radius: {\n      small: '0.075rem',\n      medium: '0.1rem',\n      large: '0.15rem',\n    },\n  },\n};\n\nconst synthwaveTheme = {\n  extend: 'dark',\n  colors: {\n    'background-title-bar': '#03010b',\n    'background-body': '#000000',\n    background: {\n      DEFAULT: '#2d1b69',\n    },\n    foreground: {\n      DEFAULT: '#f9f7fd',\n    },\n    divider: {\n      DEFAULT: 'rgba(249, 247, 253, 0.20)',\n    },\n    focus: {\n      DEFAULT: '#d95cb4',\n    },\n    content1: {\n      DEFAULT: '#080325',\n      foreground: '#EDE5FC',\n    },\n    content2: {\n      DEFAULT: '#0C062D',\n      foreground: '#DCD0F6',\n    },\n    content3: {\n      DEFAULT: '#110938',\n      foreground: '#BAA5ED',\n    },\n    content4: {\n      DEFAULT: '#180D43',\n      foreground: '#866EC9',\n    },\n    default: {\n      50: '#080325',\n      100: '#0C062D',\n      200: '#110938',\n      300: '#180D43',\n      400: '#20134E',\n      500: '#544094',\n      600: '#866EC9',\n      700: '#BAA5ED',\n      800: '#DCD0F6',\n      900: '#EDE5FC',\n      DEFAULT: '#20134E',\n      foreground: '#f9f7fd',\n    },\n    primary: {\n      50: '#54105F',\n      100: '#6F1C72',\n      200: '#8E2C89',\n      300: '#AA409A',\n      400: '#C658AB',\n      500: '#DC80BD',\n      600: '#ED9ECB',\n      700: '#F9C1DB',\n      800: '#FCE0EA',\n      900: '#FAF0F7',\n      DEFAULT: '#C658AB',\n      foreground: '#43012f',\n    },\n    secondary: {\n      50: '#0C2F63',\n      100: '#144278',\n      200: '#205E95',\n      300: '#2E7CB2',\n      400: '#409ED0',\n      500: '#6DC1E2',\n      600: '#8CDAF0',\n      700: '#B4EFFA',\n      800: '#D9F9FC',\n      900: '#EBF5FA',\n      DEFAULT: '#409ED0',\n      foreground: '#0b2f42',\n    },\n    success: {\n      50: '#155F70',\n      100: '#247F87',\n      200: '#38A8A7',\n      300: '#52C9BC',\n      400: '#71EAD2',\n      500: '#94F2D6',\n      600: '#ABF8DA',\n      700: '#C8FCE3',\n      800: '#E3FDEE',\n      900: '#F2FDFB',\n      DEFAULT: '#71EAD2',\n      foreground: '#201047',\n    },\n    warning: {\n      50: '#745609',\n      100: '#8C6B0F',\n      200: '#AE8A18',\n      300: '#D0AA23',\n      400: '#F3CC30',\n      500: '#F7DD62',\n      600: '#FBE882',\n      700: '#FDF2AC',\n      800: '#FEF9D5',\n      900: '#FEFAEC',\n      DEFAULT: '#F3CC30',\n      foreground: '#201047',\n    },\n    danger: {\n      50: '#6C0C3D',\n      100: '#831442',\n      200: '#A2204A',\n      300: '#C22E50',\n      400: '#E24056',\n      500: '#ED6D73',\n      600: '#F6918C',\n      700: '#FCBEB3',\n      800: '#FDE1D9',\n      900: '#FCEDEF',\n      DEFAULT: '#E24056',\n      foreground: '#f9f7fd',\n    },\n  },\n};\n\nconst retroTheme = {\n  extend: 'light',\n  colors: {\n    'background-title-bar': '#fffefc',\n    'background-body': '#ffffff',\n    background: {\n      DEFAULT: '#e2d8b3',\n    },\n    foreground: {\n      DEFAULT: '#282425',\n    },\n    divider: {\n      DEFAULT: 'rgba(40, 36, 37, 0.25)',\n    },\n    focus: {\n      DEFAULT: '#e27c76',\n    },\n    content1: {\n      DEFAULT: '#FDFBF4',\n      foreground: '#3B2911',\n    },\n    content2: {\n      DEFAULT: '#F8F5EA',\n      foreground: '#48361C',\n    },\n    content3: {\n      DEFAULT: '#F2ECD6',\n      foreground: '#59492C',\n    },\n    content4: {\n      DEFAULT: '#D8CEB2',\n      foreground: '#6B5D41',\n    },\n    default: {\n      50: '#FDFBF4',\n      100: '#F8F5EA',\n      200: '#F2ECD6',\n      300: '#D8CEB2',\n      400: '#B1A68A',\n      500: '#7D7259',\n      600: '#6B5D41',\n      700: '#59492C',\n      800: '#48361C',\n      900: '#3B2911',\n      DEFAULT: '#D8CEB2',\n      foreground: '#48361C',\n    },\n    primary: {\n      50: '#FAF0F0',\n      100: '#FCECE4',\n      200: '#FAD5CA',\n      300: '#F0B5AB',\n      400: '#E19491',\n      500: '#CD6C70',\n      600: '#B04E5B',\n      700: '#93364A',\n      800: '#76223C',\n      900: '#621433',\n      DEFAULT: '#CD6C70',\n      foreground: '#282425',\n    },\n    secondary: {\n      50: '#F2F7F5',\n      100: '#EBFAEC',\n      200: '#D7F6DE',\n      300: '#BAE6C8',\n      400: '#9DCEB1',\n      500: '#77AE93',\n      600: '#56957D',\n      700: '#3B7D6A',\n      800: '#256458',\n      900: '#16534E',\n      DEFAULT: '#77AE93',\n      foreground: '#282425',\n    },\n    success: {\n      50: '#E4FBED',\n      100: '#D0FACF',\n      200: '#A0F5A6',\n      300: '#6DE381',\n      400: '#46C769',\n      500: '#16A34A',\n      600: '#108C4A',\n      700: '#0B7547',\n      800: '#075E41',\n      900: '#044E3C',\n      DEFAULT: '#16A34A',\n      foreground: '#c6ffd5',\n    },\n    warning: {\n      50: '#FEF1E1',\n      100: '#FDEFCB',\n      200: '#FBDB99',\n      300: '#F3BE65',\n      400: '#E8A13E',\n      500: '#D97706',\n      600: '#BA5D04',\n      700: '#9C4603',\n      800: '#7D3201',\n      900: '#682501',\n      DEFAULT: '#D97706',\n      foreground: '#2b1800',\n    },\n    danger: {\n      50: '#FBE9E9',\n      100: '#FDE1D3',\n      200: '#FBBDA8',\n      300: '#F4907B',\n      400: '#EA6558',\n      500: '#DC2626',\n      600: '#BD1B29',\n      700: '#9E132B',\n      800: '#7F0C2A',\n      900: '#690729',\n      DEFAULT: '#DC2626',\n      foreground: '#facdcd',\n    },\n  },\n  layout: {\n    radius: {\n      small: '0.225rem',\n      medium: '0.3rem',\n      large: '0.45rem',\n    },\n  },\n};\n\nconst cyberpunkTheme = {\n  extend: 'light',\n  colors: {\n    'background-title-bar': '#fffff1',\n    'background-body': '#ffffff',\n    background: {\n      DEFAULT: '#fcee00',\n    },\n    foreground: {\n      DEFAULT: '#302d11',\n    },\n    divider: {\n      DEFAULT: 'rgba(48, 45, 17, 0.25)',\n    },\n    focus: {\n      DEFAULT: '#f4547e',\n    },\n    content1: {\n      DEFAULT: '#FFFDD1',\n      foreground: '#0F0F00',\n    },\n    content2: {\n      DEFAULT: '#FFFBA8',\n      foreground: '#191800',\n    },\n    content3: {\n      DEFAULT: '#FFF64D',\n      foreground: '#292700',\n    },\n    content4: {\n      DEFAULT: '#F5E900',\n      foreground: '#333000',\n    },\n    default: {\n      50: '#FFFDD1',\n      100: '#FFFBA8',\n      200: '#FFF64D',\n      300: '#F5E900',\n      400: '#999100',\n      500: '#423F00',\n      600: '#333000',\n      700: '#292700',\n      800: '#191800',\n      900: '#0F0F00',\n      DEFAULT: '#F5E900',\n      foreground: '#191800',\n    },\n    primary: {\n      50: '#FEF1F4',\n      100: '#FDE2EA',\n      200: '#FBCBD8',\n      300: '#F9AEC2',\n      400: '#F792AD',\n      500: '#F57799',\n      600: '#F03365',\n      700: '#CC0F41',\n      800: '#8A0A2C',\n      900: '#430515',\n      DEFAULT: '#F57799',\n      foreground: '#2f1c20',\n    },\n    secondary: {\n      50: '#F1FAFD',\n      100: '#E8F7FC',\n      200: '#CDECF9',\n      300: '#B6E4F6',\n      400: '#9BDAF3',\n      500: '#84D1F0',\n      600: '#40B8E8',\n      700: '#1896C8',\n      800: '#106384',\n      900: '#083344',\n      DEFAULT: '#84D1F0',\n      foreground: '#1f2a2f',\n    },\n    success: {\n      50: '#EFFBF5',\n      100: '#DFF6EB',\n      200: '#BBECD4',\n      300: '#9BE4C0',\n      400: '#7BDBAC',\n      500: '#59D298',\n      600: '#33BD7A',\n      700: '#268C5B',\n      800: '#195C3C',\n      900: '#0D301F',\n      DEFAULT: '#59D298',\n      foreground: '#0b3320',\n    },\n    warning: {\n      50: '#FEF8E7',\n      100: '#FDF1CE',\n      200: '#FBE5A2',\n      300: '#F8D772',\n      400: '#F6CA46',\n      500: '#F4BE15',\n      600: '#CD9C0A',\n      700: '#977307',\n      800: '#664E05',\n      900: '#312502',\n      DEFAULT: '#F4BE15',\n      foreground: '#362800',\n    },\n    danger: {\n      50: '#FDF1F1',\n      100: '#FCE4E4',\n      200: '#F8C8C8',\n      300: '#F5A9A9',\n      400: '#F18D8D',\n      500: '#EE7372',\n      600: '#E63232',\n      700: '#BB1616',\n      800: '#7F0F0F',\n      900: '#400808',\n      DEFAULT: '#EE7372',\n      foreground: '#430100',\n    },\n  },\n  layout: {\n    radius: {\n      small: '0.075rem',\n      medium: '0.1rem',\n      large: '0.15rem',\n    },\n  },\n};\n\nconst valentineTheme = {\n  extend: 'light',\n  colors: {\n    'background-title-bar': '#fdfafc',\n    'background-body': '#ffffff',\n    background: {\n      DEFAULT: '#eed6e8',\n    },\n    foreground: {\n      DEFAULT: '#5f2d3b',\n    },\n    divider: {\n      DEFAULT: 'rgba(95, 45, 59, 0.25)',\n    },\n    focus: {\n      DEFAULT: '#da4f60',\n    },\n    content1: {\n      DEFAULT: '#F7EDF2',\n      foreground: '#200E16',\n    },\n    content2: {\n      DEFAULT: '#EED8E1',\n      foreground: '#441D2E',\n    },\n    content3: {\n      DEFAULT: '#DFB4C7',\n      foreground: '#642A44',\n    },\n    content4: {\n      DEFAULT: '#CF8CA9',\n      foreground: '#883A5C',\n    },\n    default: {\n      50: '#F7EDF2',\n      100: '#EED8E1',\n      200: '#DFB4C7',\n      300: '#CF8CA9',\n      400: '#BF688E',\n      500: '#A84771',\n      600: '#883A5C',\n      700: '#642A44',\n      800: '#441D2E',\n      900: '#200E16',\n      DEFAULT: '#CF8CA9',\n      foreground: '#441D2E',\n    },\n    primary: {\n      50: '#FCF2F4',\n      100: '#F9E2E4',\n      200: '#F2C4CA',\n      300: '#ECA7AF',\n      400: '#E68994',\n      500: '#E06E7C',\n      600: '#D43548',\n      700: '#A42332',\n      800: '#6D1721',\n      900: '#370C11',\n      DEFAULT: '#E06E7C',\n      foreground: '#2b1b1c',\n    },\n    secondary: {\n      50: '#F8F5FE',\n      100: '#ECE7FD',\n      200: '#DDD4FC',\n      300: '#CBBCFB',\n      400: '#BCA9F9',\n      500: '#A992F8',\n      600: '#7149F3',\n      700: '#3E0EDC',\n      800: '#2A0A95',\n      900: '#140548',\n      DEFAULT: '#A992F8',\n      foreground: '#231f2e',\n    },\n    success: {\n      50: '#E9F7EA',\n      100: '#D6F0D8',\n      200: '#A9E0AE',\n      300: '#80D188',\n      400: '#53C15E',\n      500: '#3AA145',\n      600: '#2E7F36',\n      700: '#236129',\n      800: '#17401B',\n      900: '#0C220E',\n      DEFAULT: '#3AA145',\n      foreground: '#daedda',\n    },\n    warning: {\n      50: '#FFF2E0',\n      100: '#FFE4C2',\n      200: '#FFCC8A',\n      300: '#FFB24D',\n      400: '#FF970F',\n      500: '#D37800',\n      600: '#A85F00',\n      700: '#804800',\n      800: '#573100',\n      900: '#291700',\n      DEFAULT: '#D37800',\n      foreground: '#2b1c0b',\n    },\n    danger: {\n      50: '#FBEAEA',\n      100: '#F7D5D4',\n      200: '#EEABAA',\n      300: '#E6817F',\n      400: '#DD5250',\n      500: '#D22C29',\n      600: '#A62321',\n      700: '#801B19',\n      800: '#551211',\n      900: '#2B0908',\n      DEFAULT: '#D22C29',\n      foreground: '#fbdad1',\n    },\n  },\n  layout: {\n    radius: {\n      small: '0.75rem',\n      medium: '1rem',\n      large: '1.5rem',\n    },\n  },\n};\n\nconst halloweenTheme = {\n  extend: 'dark',\n  colors: {\n    'background-title-bar': '#020202',\n    'background-body': '#000000',\n    background: {\n      DEFAULT: '#212121',\n    },\n    foreground: {\n      DEFAULT: '#cfcfcf',\n    },\n    divider: {\n      DEFAULT: 'rgba(207, 207, 207, 0.20)',\n    },\n    focus: {\n      DEFAULT: '#d17a03',\n    },\n    content1: {\n      DEFAULT: '#080808',\n      foreground: '#E8E8E8',\n    },\n    content2: {\n      DEFAULT: '#0D0D0D',\n      foreground: '#D4D4D4',\n    },\n    content3: {\n      DEFAULT: '#141414',\n      foreground: '#A6A6A6',\n    },\n    content4: {\n      DEFAULT: '#1A1A1A',\n      foreground: '#7A7A7A',\n    },\n    default: {\n      50: '#080808',\n      100: '#0D0D0D',\n      200: '#141414',\n      300: '#1A1A1A',\n      400: '#212121',\n      500: '#4D4D4D',\n      600: '#7A7A7A',\n      700: '#A6A6A6',\n      800: '#D4D4D4',\n      900: '#E8E8E8',\n      DEFAULT: '#212121',\n      foreground: '#cfcfcf',\n    },\n    primary: {\n      50: '#301C03',\n      100: '#5F3907',\n      200: '#8A530A',\n      300: '#BA6F0D',\n      400: '#E98C10',\n      500: '#F2A33B',\n      600: '#F5B96B',\n      700: '#F8D2A0',\n      800: '#FCE8CF',\n      900: '#FDF4E7',\n      DEFAULT: '#E98C10',\n      foreground: '#131616',\n    },\n    secondary: {\n      50: '#160C21',\n      100: '#2A183F',\n      200: '#402461',\n      300: '#542F7E',\n      400: '#6A3B9E',\n      500: '#8654BF',\n      600: '#A681D0',\n      700: '#C3AADF',\n      800: '#E2D6F0',\n      900: '#EFE9F7',\n      DEFAULT: '#6A3B9E',\n      foreground: '#e1d5ec',\n    },\n    success: {\n      50: '#0C220E',\n      100: '#17401B',\n      200: '#236129',\n      300: '#2E7F36',\n      400: '#3AA145',\n      500: '#53C15E',\n      600: '#80D188',\n      700: '#A9E0AE',\n      800: '#D6F0D8',\n      900: '#E9F7EA',\n      DEFAULT: '#3AA145',\n      foreground: '#daedda',\n    },\n    warning: {\n      50: '#291700',\n      100: '#573100',\n      200: '#804800',\n      300: '#A85F00',\n      400: '#D37800',\n      500: '#FF970F',\n      600: '#FFB24D',\n      700: '#FFCC8A',\n      800: '#FFE4C2',\n      900: '#FFF2E0',\n      DEFAULT: '#D37800',\n      foreground: '#2b1c0b',\n    },\n    danger: {\n      50: '#2B0908',\n      100: '#551211',\n      200: '#801B19',\n      300: '#A62321',\n      400: '#D22C29',\n      500: '#DD5250',\n      600: '#E6817F',\n      700: '#EEABAA',\n      800: '#F7D5D4',\n      900: '#FBEAEA',\n      DEFAULT: '#D22C29',\n      foreground: '#fbdad1',\n    },\n  },\n};\n\nconst gardenTheme = {\n  extend: 'light',\n  colors: {\n    'background-title-bar': '#fffcf1',\n    'background-body': '#ffffff',\n    background: {\n      DEFAULT: '#e9e7e7',\n    },\n    foreground: {\n      DEFAULT: '#100f0f',\n    },\n    divider: {\n      DEFAULT: 'rgba(16, 15, 15, 0.25)',\n    },\n    focus: {\n      DEFAULT: '#c70c66',\n    },\n    content1: {\n      DEFAULT: '#FFF4D1',\n      foreground: '#0A0800',\n    },\n    content2: {\n      DEFAULT: '#FFE8A3',\n      foreground: '#0F0B00',\n    },\n    content3: {\n      DEFAULT: '#FFD042',\n      foreground: '#191300',\n    },\n    content4: {\n      DEFAULT: '#E6AC00',\n      foreground: '#1F1700',\n    },\n    default: {\n      50: '#FFF4D1',\n      100: '#FFE8A3',\n      200: '#FFD042',\n      300: '#E6AC00',\n      400: '#856300',\n      500: '#281E00',\n      600: '#1F1700',\n      700: '#191300',\n      800: '#0F0B00',\n      900: '#0A0800',\n      DEFAULT: '#E6AC00',\n      foreground: '#0F0B00',\n    },\n    primary: {\n      50: '#FDE7F2',\n      100: '#FCCFE5',\n      200: '#F8A0CB',\n      300: '#F56BAE',\n      400: '#F23B93',\n      500: '#E91078',\n      600: '#BA0D61',\n      700: '#8A0A48',\n      800: '#5F0732',\n      900: '#300319',\n      DEFAULT: '#E91078',\n      foreground: '#fcdbe4',\n    },\n    secondary: {\n      50: '#F5EAEF',\n      100: '#EBD6E0',\n      200: '#D7ADC0',\n      300: '#C383A1',\n      400: '#AF5A82',\n      500: '#8A4364',\n      600: '#6E3550',\n      700: '#52283C',\n      800: '#371B28',\n      900: '#1B0D14',\n      DEFAULT: '#8A4364',\n      foreground: '#e8d7dd',\n    },\n    success: {\n      50: '#EEFEEC',\n      100: '#DFFCE3',\n      200: '#C0FACD',\n      300: '#9EF1B8',\n      400: '#81E3AA',\n      500: '#59D298',\n      600: '#41B488',\n      700: '#2C9779',\n      800: '#1C7969',\n      900: '#11645E',\n      DEFAULT: '#59D298',\n      foreground: '#0b3320',\n    },\n    warning: {\n      50: '#FEFBE2',\n      100: '#FEF7CF',\n      200: '#FDEEA1',\n      300: '#FBE171',\n      400: '#F8D34E',\n      500: '#F4BE15',\n      600: '#D19D0F',\n      700: '#AF7F0A',\n      800: '#8D6206',\n      900: '#754E04',\n      DEFAULT: '#F4BE15',\n      foreground: '#362800',\n    },\n    danger: {\n      50: '#FEF6EE',\n      100: '#FEEDE3',\n      200: '#FDD6C8',\n      300: '#F9B9AB',\n      400: '#F49E95',\n      500: '#EE7372',\n      600: '#CC535C',\n      700: '#AB394C',\n      800: '#8A243D',\n      900: '#721534',\n      DEFAULT: '#EE7372',\n      foreground: '#430100',\n    },\n  },\n};\n\nconst forestTheme = {\n  extend: 'dark',\n  colors: {\n    'background-title-bar': '#020302',\n    'background-body': '#000000',\n    background: {\n      DEFAULT: '#161212',\n    },\n    foreground: {\n      DEFAULT: '#cac9c9',\n    },\n    divider: {\n      DEFAULT: 'rgba(202, 201, 201, 0.20)',\n    },\n    focus: {\n      DEFAULT: '#389943',\n    },\n    content1: {\n      DEFAULT: '#050A08',\n      foreground: '#E4F1EC',\n    },\n    content2: {\n      DEFAULT: '#0B1411',\n      foreground: '#C7E1D7',\n    },\n    content3: {\n      DEFAULT: '#101E19',\n      foreground: '#8EC2AF',\n    },\n    content4: {\n      DEFAULT: '#172B24',\n      foreground: '#57A287',\n    },\n    default: {\n      50: '#050A08',\n      100: '#0B1411',\n      200: '#101E19',\n      300: '#172B24',\n      400: '#1C342B',\n      500: '#3B6D5B',\n      600: '#57A287',\n      700: '#8EC2AF',\n      800: '#C7E1D7',\n      900: '#E4F1EC',\n      DEFAULT: '#1C342B',\n      foreground: '#ccd2cf',\n    },\n    primary: {\n      50: '#0E2510',\n      100: '#1C4A20',\n      200: '#297030',\n      300: '#379540',\n      400: '#44B850',\n      500: '#6AC873',\n      600: '#8FD696',\n      700: '#B5E3B9',\n      800: '#DAF1DC',\n      900: '#ECF8EE',\n      DEFAULT: '#44B850',\n      foreground: '#000000',\n    },\n    secondary: {\n      50: '#0E251D',\n      100: '#1C4A39',\n      200: '#2A6F56',\n      300: '#389472',\n      400: '#46B98E',\n      500: '#6BC7A5',\n      600: '#90D5BC',\n      700: '#B5E3D2',\n      800: '#DAF1E9',\n      900: '#ECF8F4',\n      DEFAULT: '#46B98E',\n      foreground: '#16251e',\n    },\n    success: {\n      50: '#11645E',\n      100: '#1C7969',\n      200: '#2C9779',\n      300: '#41B488',\n      400: '#59D298',\n      500: '#81E3AA',\n      600: '#9EF1B8',\n      700: '#C0FACD',\n      800: '#DFFCE3',\n      900: '#EEFEEC',\n      DEFAULT: '#59D298',\n      foreground: '#0b3320',\n    },\n    warning: {\n      50: '#754E04',\n      100: '#8D6206',\n      200: '#AF7F0A',\n      300: '#D19D0F',\n      400: '#F4BE15',\n      500: '#F8D34E',\n      600: '#FBE171',\n      700: '#FDEEA1',\n      800: '#FEF7CF',\n      900: '#FEFBE2',\n      DEFAULT: '#F4BE15',\n      foreground: '#362800',\n    },\n    danger: {\n      50: '#721534',\n      100: '#8A243D',\n      200: '#AB394C',\n      300: '#CC535C',\n      400: '#EE7372',\n      500: '#F49E95',\n      600: '#F9B9AB',\n      700: '#FDD6C8',\n      800: '#FEEDE3',\n      900: '#FEF6EE',\n      DEFAULT: '#EE7372',\n      foreground: '#430100',\n    },\n  },\n  layout: {\n    radius: {\n      small: '0.75rem',\n      medium: '1rem',\n      large: '1.5rem',\n    },\n  },\n};\n\nconst aquaTheme = {\n  extend: 'light',\n  colors: {\n    'background-title-bar': '#fafcfe',\n    'background-body': '#ffffff',\n    background: {\n      DEFAULT: '#3c5ca9',\n    },\n    foreground: {\n      DEFAULT: '#0E1D2A',\n    },\n    divider: {\n      DEFAULT: 'rgba(217, 221, 238, 0.25)',\n    },\n    focus: {\n      DEFAULT: '#48c8cf',\n    },\n    content1: {\n      DEFAULT: '#ECF2F9',\n      foreground: '#0E1D2A',\n    },\n    content2: {\n      DEFAULT: '#DCE8F4',\n      foreground: '#1A3651',\n    },\n    content3: {\n      DEFAULT: '#B6CFE7',\n      foreground: '#28537B',\n    },\n    content4: {\n      DEFAULT: '#93B9DC',\n      foreground: '#3570A6',\n    },\n    default: {\n      50: '#ECF2F9',\n      100: '#DCE8F4',\n      200: '#B6CFE7',\n      300: '#93B9DC',\n      400: '#70A2D1',\n      500: '#4B8BC5',\n      600: '#3570A6',\n      700: '#28537B',\n      800: '#1A3651',\n      900: '#0E1D2A',\n      DEFAULT: '#93B9DC',\n      foreground: '#1A3651',\n    },\n    primary: {\n      50: '#ECFDFD',\n      100: '#DEFBFC',\n      200: '#BDF7FA',\n      300: '#97F2F6',\n      400: '#76EDF4',\n      500: '#55E9F1',\n      600: '#18E1EC',\n      700: '#0FABB3',\n      800: '#0A757B',\n      900: '#053A3D',\n      DEFAULT: '#55E9F1',\n      foreground: '#195557',\n    },\n    secondary: {\n      50: '#F5F2F8',\n      100: '#E8E1EF',\n      200: '#D4C6E1',\n      300: '#BDA8D1',\n      400: '#A98DC3',\n      500: '#9370B4',\n      600: '#77519A',\n      700: '#583C72',\n      800: '#3B284D',\n      900: '#1C1325',\n      DEFAULT: '#9370B4',\n      foreground: '#1f1924',\n    },\n    success: {\n      50: '#E9F7EA',\n      100: '#D6F0D8',\n      200: '#A9E0AE',\n      300: '#80D188',\n      400: '#53C15E',\n      500: '#3AA145',\n      600: '#2E7F36',\n      700: '#236129',\n      800: '#17401B',\n      900: '#0C220E',\n      DEFAULT: '#3AA145',\n      foreground: '#daedda',\n    },\n    warning: {\n      50: '#FFF2E0',\n      100: '#FFE4C2',\n      200: '#FFCC8A',\n      300: '#FFB24D',\n      400: '#FF970F',\n      500: '#D37800',\n      600: '#A85F00',\n      700: '#804800',\n      800: '#573100',\n      900: '#291700',\n      DEFAULT: '#D37800',\n      foreground: '#2b1c0b',\n    },\n    danger: {\n      50: '#FBEAEA',\n      100: '#F7D5D4',\n      200: '#EEABAA',\n      300: '#E6817F',\n      400: '#DD5250',\n      500: '#D22C29',\n      600: '#A62321',\n      700: '#801B19',\n      800: '#551211',\n      900: '#2B0908',\n      DEFAULT: '#D22C29',\n      foreground: '#fbdad1',\n    },\n  },\n};\n\nconst lofiTheme = {\n  extend: 'light',\n  colors: {\n    'background-title-bar': '#f8f8f8',\n    'background-body': '#ffffff',\n    background: {\n      DEFAULT: '#ffffff',\n    },\n    foreground: {\n      DEFAULT: '#000000',\n    },\n    divider: {\n      DEFAULT: 'rgba(0, 0, 0, 0.25)',\n    },\n    focus: {\n      DEFAULT: '#000000',\n    },\n    content1: {\n      DEFAULT: '#E6E6E6',\n      foreground: '#000000',\n    },\n    content2: {\n      DEFAULT: '#CCCCCC',\n      foreground: '#000000',\n    },\n    content3: {\n      DEFAULT: '#999999',\n      foreground: '#000000',\n    },\n    content4: {\n      DEFAULT: '#666666',\n      foreground: '#000000',\n    },\n    default: {\n      50: '#E6E6E6',\n      100: '#CCCCCC',\n      200: '#999999',\n      300: '#666666',\n      400: '#333333',\n      500: '#000000',\n      600: '#000000',\n      700: '#000000',\n      800: '#000000',\n      900: '#000000',\n      DEFAULT: '#666666',\n      foreground: '#000000',\n    },\n    primary: {\n      50: '#E8E8E8',\n      100: '#CFCFCF',\n      200: '#9E9E9E',\n      300: '#6E6E6E',\n      400: '#3D3D3D',\n      500: '#0D0D0D',\n      600: '#0A0A0A',\n      700: '#080808',\n      800: '#050505',\n      900: '#030303',\n      DEFAULT: '#0D0D0D',\n      foreground: '#ffffff',\n    },\n    secondary: {\n      50: '#E9E8E8',\n      100: '#D2D0D0',\n      200: '#A5A1A1',\n      300: '#787373',\n      400: '#494646',\n      500: '#1A1919',\n      600: '#151414',\n      700: '#100F0F',\n      800: '#0A0A0A',\n      900: '#050505',\n      DEFAULT: '#1A1919',\n      foreground: '#ffffff',\n    },\n    success: {\n      50: '#EBF9EB',\n      100: '#DCF4DC',\n      200: '#B8EAB8',\n      300: '#91DE91',\n      400: '#6ED36E',\n      500: '#4BC94B',\n      600: '#33A933',\n      700: '#267E26',\n      800: '#1A561A',\n      900: '#0D2B0D',\n      DEFAULT: '#4BC94B',\n      foreground: '#000000',\n    },\n    warning: {\n      50: '#FEEEEC',\n      100: '#FDE1DD',\n      200: '#FBC2BC',\n      300: '#F89F96',\n      400: '#F68174',\n      500: '#F46252',\n      600: '#F02A14',\n      700: '#B61D0C',\n      800: '#7D1408',\n      900: '#3E0A04',\n      DEFAULT: '#F46252',\n      foreground: '#ffffff',\n    },\n    danger: {\n      50: '#FBE9F4',\n      100: '#F8D3EA',\n      200: '#F0A3D2',\n      300: '#E977BD',\n      400: '#E24BA8',\n      500: '#D4228F',\n      600: '#AB1B74',\n      700: '#7F1456',\n      800: '#540D39',\n      900: '#2C071E',\n      DEFAULT: '#D4228F',\n      foreground: '#ffffff',\n    },\n  },\n};\n\nconst pastelTheme = {\n  extend: 'light',\n  colors: {\n    'background-title-bar': '#fbfdfe',\n    'background-body': '#ffffff',\n    background: {\n      DEFAULT: '#ffffff',\n    },\n    foreground: {\n      DEFAULT: '#303030',\n    },\n    divider: {\n      DEFAULT: 'rgba(48, 48, 48, 0.25)',\n    },\n    focus: {\n      DEFAULT: '#bfabca',\n    },\n    content1: {\n      DEFAULT: '#F1F6F9',\n      foreground: '#13252F',\n    },\n    content2: {\n      DEFAULT: '#E6EFF5',\n      foreground: '#25475A',\n    },\n    content3: {\n      DEFAULT: '#C9DDE9',\n      foreground: '#386C89',\n    },\n    content4: {\n      DEFAULT: '#AFCDDE',\n      foreground: '#4A8EB5',\n    },\n    default: {\n      50: '#F1F6F9',\n      100: '#E6EFF5',\n      200: '#C9DDE9',\n      300: '#AFCDDE',\n      400: '#93BBD2',\n      500: '#79ABC8',\n      600: '#4A8EB5',\n      700: '#386C89',\n      800: '#25475A',\n      900: '#13252F',\n      DEFAULT: '#AFCDDE',\n      foreground: '#25475A',\n    },\n    primary: {\n      50: '#FAF9FB',\n      100: '#F5F2F7',\n      200: '#ECE6EF',\n      300: '#E2D9E7',\n      400: '#D9CDE0',\n      500: '#D0C1D8',\n      600: '#A98EB8',\n      700: '#835E96',\n      800: '#573F64',\n      900: '#2C1F32',\n      DEFAULT: '#D0C1D8',\n      foreground: '#1c2728',\n    },\n    secondary: {\n      50: '#FEFBFB',\n      100: '#FCF3F4',\n      200: '#FAEBED',\n      300: '#F7DEE2',\n      400: '#F5D6DB',\n      500: '#F2CBD1',\n      600: '#E08592',\n      700: '#CE3B51',\n      800: '#8F2434',\n      900: '#451119',\n      DEFAULT: '#F2CBD1',\n      foreground: '#2e2829',\n    },\n    success: {\n      50: '#EEFEEC',\n      100: '#DFFCE3',\n      200: '#C0FACD',\n      300: '#9EF1B8',\n      400: '#81E3AA',\n      500: '#59D298',\n      600: '#41B488',\n      700: '#2C9779',\n      800: '#1C7969',\n      900: '#11645E',\n      DEFAULT: '#59D298',\n      foreground: '#0b3320',\n    },\n    warning: {\n      50: '#FEFBE2',\n      100: '#FEF7CF',\n      200: '#FDEEA1',\n      300: '#FBE171',\n      400: '#F8D34E',\n      500: '#F4BE15',\n      600: '#D19D0F',\n      700: '#AF7F0A',\n      800: '#8D6206',\n      900: '#754E04',\n      DEFAULT: '#F4BE15',\n      foreground: '#362800',\n    },\n    danger: {\n      50: '#FEF6EE',\n      100: '#FEEDE3',\n      200: '#FDD6C8',\n      300: '#F9B9AB',\n      400: '#F49E95',\n      500: '#EE7372',\n      600: '#CC535C',\n      700: '#AB394C',\n      800: '#8A243D',\n      900: '#721534',\n      DEFAULT: '#EE7372',\n      foreground: '#430100',\n    },\n  },\n  layout: {\n    radius: {\n      small: '0.75rem',\n      medium: '1rem',\n      large: '1.5rem',\n    },\n  },\n};\n\nconst fantasyTheme = {\n  extend: 'light',\n  colors: {\n    'background-title-bar': '#f8f9fb',\n    'background-body': '#ffffff',\n    background: {\n      DEFAULT: '#ffffff',\n    },\n    foreground: {\n      DEFAULT: '#212938',\n    },\n    divider: {\n      DEFAULT: 'rgba(33, 41, 56, 0.25)',\n    },\n    focus: {\n      DEFAULT: '#4b0a55',\n    },\n    content1: {\n      DEFAULT: '#E5E9F0',\n      foreground: '#08090D',\n    },\n    content2: {\n      DEFAULT: '#CCD3E1',\n      foreground: '#0D1016',\n    },\n    content3: {\n      DEFAULT: '#95A4C1',\n      foreground: '#151A23',\n    },\n    content4: {\n      DEFAULT: '#6278A2',\n      foreground: '#1A212D',\n    },\n    default: {\n      50: '#E5E9F0',\n      100: '#CCD3E1',\n      200: '#95A4C1',\n      300: '#6278A2',\n      400: '#40506D',\n      500: '#212938',\n      600: '#1A212D',\n      700: '#151A23',\n      800: '#0D1016',\n      900: '#08090D',\n      DEFAULT: '#6278A2',\n      foreground: '#0D1016',\n    },\n    primary: {\n      50: '#F7DFFB',\n      100: '#EEBBF6',\n      200: '#DE77EE',\n      300: '#CE38E6',\n      400: '#A418B9',\n      500: '#690F76',\n      600: '#540C5F',\n      700: '#400948',\n      800: '#28062D',\n      900: '#140317',\n      DEFAULT: '#690F76',\n      foreground: '#e1cfe3',\n    },\n    secondary: {\n      50: '#EAF3FA',\n      100: '#D2E5F4',\n      200: '#A4CCEA',\n      300: '#7BB4E0',\n      400: '#4E9AD5',\n      500: '#2D7EBE',\n      600: '#246699',\n      700: '#1B4D73',\n      800: '#12324A',\n      900: '#091925',\n      DEFAULT: '#2D7EBE',\n      foreground: '#dae5f3',\n    },\n    success: {\n      50: '#EEFEEC',\n      100: '#DFFCE3',\n      200: '#C0FACD',\n      300: '#9EF1B8',\n      400: '#81E3AA',\n      500: '#59D298',\n      600: '#41B488',\n      700: '#2C9779',\n      800: '#1C7969',\n      900: '#11645E',\n      DEFAULT: '#59D298',\n      foreground: '#0b3320',\n    },\n    warning: {\n      50: '#FEFBE2',\n      100: '#FEF7CF',\n      200: '#FDEEA1',\n      300: '#FBE171',\n      400: '#F8D34E',\n      500: '#F4BE15',\n      600: '#D19D0F',\n      700: '#AF7F0A',\n      800: '#8D6206',\n      900: '#754E04',\n      DEFAULT: '#F4BE15',\n      foreground: '#362800',\n    },\n    danger: {\n      50: '#FEF6EE',\n      100: '#FEEDE3',\n      200: '#FDD6C8',\n      300: '#F9B9AB',\n      400: '#F49E95',\n      500: '#EE7372',\n      600: '#CC535C',\n      700: '#AB394C',\n      800: '#8A243D',\n      900: '#721534',\n      DEFAULT: '#EE7372',\n      foreground: '#430100',\n    },\n  },\n};\n\nconst wireframeTheme = {\n  extend: 'light',\n  colors: {\n    'background-title-bar': '#fdfdfd',\n    'background-body': '#ffffff',\n    background: {\n      DEFAULT: '#ffffff',\n    },\n    foreground: {\n      DEFAULT: '#303030',\n    },\n    divider: {\n      DEFAULT: 'rgba(48, 48, 48, 0.25)',\n    },\n    focus: {\n      DEFAULT: '#a6a6a6',\n    },\n    content1: {\n      DEFAULT: '#F7F7F7',\n      foreground: '#262626',\n    },\n    content2: {\n      DEFAULT: '#F2F2F2',\n      foreground: '#4D4D4D',\n    },\n    content3: {\n      DEFAULT: '#E6E6E6',\n      foreground: '#707070',\n    },\n    content4: {\n      DEFAULT: '#D6D6D6',\n      foreground: '#969696',\n    },\n    default: {\n      50: '#F7F7F7',\n      100: '#F2F2F2',\n      200: '#E6E6E6',\n      300: '#D6D6D6',\n      400: '#C9C9C9',\n      500: '#BDBDBD',\n      600: '#969696',\n      700: '#707070',\n      800: '#4D4D4D',\n      900: '#262626',\n      DEFAULT: '#D6D6D6',\n      foreground: '#4D4D4D',\n    },\n    primary: {\n      50: '#F7F7F7',\n      100: '#F0F0F0',\n      200: '#E3E3E3',\n      300: '#D4D4D4',\n      400: '#C7C7C7',\n      500: '#B8B8B8',\n      600: '#949494',\n      700: '#6E6E6E',\n      800: '#4A4A4A',\n      900: '#242424',\n      DEFAULT: '#B8B8B8',\n      foreground: '#262626',\n    },\n    secondary: {\n      50: '#F7F7F7',\n      100: '#F0F0F0',\n      200: '#E3E3E3',\n      300: '#D4D4D4',\n      400: '#C7C7C7',\n      500: '#B8B8B8',\n      600: '#949494',\n      700: '#6E6E6E',\n      800: '#4A4A4A',\n      900: '#242424',\n      DEFAULT: '#B8B8B8',\n      foreground: '#262626',\n    },\n    success: {\n      50: '#E2FFD6',\n      100: '#C9FFB3',\n      200: '#94FF66',\n      300: '#5EFF1A',\n      400: '#3DCC00',\n      500: '#277F00',\n      600: '#1F6600',\n      700: '#174D00',\n      800: '#0F3300',\n      900: '#081900',\n      DEFAULT: '#277F00',\n      foreground: '#d4e5cc',\n    },\n    warning: {\n      50: '#F6F6EE',\n      100: '#EDEDDE',\n      200: '#DBDCBC',\n      300: '#CACA9B',\n      400: '#B8B979',\n      500: '#A5A657',\n      600: '#858646',\n      700: '#646435',\n      800: '#424323',\n      900: '#212112',\n      DEFAULT: '#A5A657',\n      foreground: '#222216',\n    },\n    danger: {\n      50: '#FEE7E6',\n      100: '#FED0CD',\n      200: '#FDA09B',\n      300: '#FB6C65',\n      400: '#FA3D33',\n      500: '#F31006',\n      600: '#C20E05',\n      700: '#900B04',\n      800: '#640702',\n      900: '#320401',\n      DEFAULT: '#F31006',\n      foreground: '#fbdbcc',\n    },\n  },\n  layout: {\n    radius: {\n      small: '0.15rem',\n      medium: '0.2rem',\n      large: '0.3rem',\n    },\n  },\n};\n\nconst luxuryTheme = {\n  extend: 'dark',\n  colors: {\n    'background-title-bar': '#030100',\n    'background-body': '#000000',\n    background: {\n      DEFAULT: '#09090b',\n    },\n    foreground: {\n      DEFAULT: '#d6a549',\n    },\n    divider: {\n      DEFAULT: 'rgba(214, 165, 73, 0.20)',\n    },\n    focus: {\n      DEFAULT: '#ededed',\n    },\n    content1: {\n      DEFAULT: '#0A0500',\n      foreground: '#FFE7D1',\n    },\n    content2: {\n      DEFAULT: '#140A00',\n      foreground: '#FFD0A3',\n    },\n    content3: {\n      DEFAULT: '#1F0F00',\n      foreground: '#FFA047',\n    },\n    content4: {\n      DEFAULT: '#291400',\n      foreground: '#EB7100',\n    },\n    default: {\n      50: '#0A0500',\n      100: '#140A00',\n      200: '#1F0F00',\n      300: '#291400',\n      400: '#311800',\n      500: '#8F4500',\n      600: '#EB7100',\n      700: '#FFA047',\n      800: '#FFD0A3',\n      900: '#FFE7D1',\n      DEFAULT: '#311800',\n      foreground: '#fce6a2',\n    },\n    primary: {\n      50: '#333333',\n      100: '#666666',\n      200: '#999999',\n      300: '#CCCCCC',\n      400: '#FFFFFF',\n      500: '#FFFFFF',\n      600: '#FFFFFF',\n      700: '#FFFFFF',\n      800: '#FFFFFF',\n      900: '#FFFFFF',\n      DEFAULT: '#FFFFFF',\n      foreground: '#1d1c1e',\n    },\n    secondary: {\n      50: '#05080F',\n      100: '#0B101E',\n      200: '#0F1729',\n      300: '#141F39',\n      400: '#192747',\n      500: '#2E4884',\n      600: '#466ABE',\n      700: '#869ED4',\n      800: '#C3CEEA',\n      900: '#E1E7F4',\n      DEFAULT: '#192747',\n      foreground: '#ccced7',\n    },\n    success: {\n      50: '#1D2A09',\n      100: '#3A5412',\n      200: '#567D1C',\n      300: '#73A725',\n      400: '#90D02E',\n      500: '#A6DA58',\n      600: '#BCE382',\n      700: '#D3EDAB',\n      800: '#E9F6D5',\n      900: '#F4FAEA',\n      DEFAULT: '#90D02E',\n      foreground: '#202913',\n    },\n    warning: {\n      50: '#37340B',\n      100: '#6B6415',\n      200: '#A29720',\n      300: '#D5C72A',\n      400: '#E0D660',\n      500: '#E6DD7F',\n      600: '#EDE6A1',\n      700: '#F3EEBF',\n      800: '#F9F7E1',\n      900: '#FCFBEE',\n      DEFAULT: '#E0D660',\n      foreground: '#2d2b19',\n    },\n    danger: {\n      50: '#430505',\n      100: '#850909',\n      200: '#C80E0E',\n      300: '#F02D2D',\n      400: '#F57271',\n      500: '#F78D8D',\n      600: '#F9A9A9',\n      700: '#FBC6C6',\n      800: '#FDE2E2',\n      900: '#FEF1F1',\n      DEFAULT: '#F57271',\n      foreground: '#301c1a',\n    },\n  },\n};\n\nconst draculaTheme = {\n  extend: 'dark',\n  colors: {\n    'background-title-bar': '#04050d',\n    'background-body': '#000000',\n    background: {\n      DEFAULT: '#282a36',\n    },\n    foreground: {\n      DEFAULT: '#f8f8f2',\n    },\n    divider: {\n      DEFAULT: 'rgba(248, 248, 242, 0.20)',\n    },\n    focus: {\n      DEFAULT: '#f459b8',\n    },\n    content1: {\n      DEFAULT: '#0C0F2A',\n      foreground: '#F4F6FC',\n    },\n    content2: {\n      DEFAULT: '#141833',\n      foreground: '#E9ECF6',\n    },\n    content3: {\n      DEFAULT: '#20243F',\n      foreground: '#D5DBEE',\n    },\n    content4: {\n      DEFAULT: '#2F334B',\n      foreground: '#ACB3CC',\n    },\n    default: {\n      50: '#0C0F2A',\n      100: '#141833',\n      200: '#20243F',\n      300: '#2F334B',\n      400: '#414558',\n      500: '#7C829A',\n      600: '#ACB3CC',\n      700: '#D5DBEE',\n      800: '#E9ECF6',\n      900: '#F4F6FC',\n      DEFAULT: '#414558',\n      foreground: '#c4cbf5',\n    },\n    primary: {\n      50: '#631069',\n      100: '#7F1C7B',\n      200: '#9D2C8E',\n      300: '#BC409F',\n      400: '#DB58B0',\n      500: '#E980BC',\n      600: '#F49CC6',\n      700: '#FBBFD5',\n      800: '#FDDEE7',\n      900: '#FBEEF7',\n      DEFAULT: '#DB58B0',\n      foreground: '#47012c',\n    },\n    secondary: {\n      50: '#261466',\n      100: '#38227C',\n      200: '#52359A',\n      300: '#704EB8',\n      400: '#926BD6',\n      500: '#B290E6',\n      600: '#C9A9F2',\n      700: '#E0C8FA',\n      800: '#F0E3FC',\n      900: '#F3EFFB',\n      DEFAULT: '#926BD6',\n      foreground: '#200150',\n    },\n    success: {\n      50: '#0F7752',\n      100: '#19905A',\n      200: '#28B366',\n      300: '#3AD771',\n      400: '#50FA7B',\n      500: '#7BFB8F',\n      600: '#96FD9C',\n      700: '#BBFEB9',\n      800: '#E0FEDC',\n      900: '#EBFEF0',\n      DEFAULT: '#50FA7B',\n      foreground: '#10420e',\n    },\n    warning: {\n      50: '#6D771A',\n      100: '#86902C',\n      200: '#A9B346',\n      300: '#CCD766',\n      400: '#F1FA8C',\n      500: '#F5FBA8',\n      600: '#F9FDBA',\n      700: '#FBFED1',\n      800: '#FDFEE8',\n      900: '#FEFFF5',\n      DEFAULT: '#F1FA8C',\n      foreground: '#484e00',\n    },\n    danger: {\n      50: '#7A1035',\n      100: '#931B3A',\n      200: '#B72A43',\n      300: '#DB3E4B',\n      400: '#FF5555',\n      500: '#FF8A7F',\n      600: '#FFAA99',\n      700: '#FFCCBB',\n      800: '#FFE8DD',\n      900: '#FFF0F0',\n      DEFAULT: '#FF5555',\n      foreground: '#401d23',\n    },\n  },\n};\n\nconst cmykTheme = {\n  extend: 'light',\n  colors: {\n    'background-title-bar': '#f9f9f9',\n    'background-body': '#ffffff',\n    background: {\n      DEFAULT: '#ffffff',\n    },\n    foreground: {\n      DEFAULT: '#303030',\n    },\n    divider: {\n      DEFAULT: 'rgba(48, 48, 48, 0.25)',\n    },\n    focus: {\n      DEFAULT: '#459eec',\n    },\n    content1: {\n      DEFAULT: '#E8E8E8',\n      foreground: '#050505',\n    },\n    content2: {\n      DEFAULT: '#D1D1D1',\n      foreground: '#0A0A0A',\n    },\n    content3: {\n      DEFAULT: '#A3A3A3',\n      foreground: '#0F0F0F',\n    },\n    content4: {\n      DEFAULT: '#757575',\n      foreground: '#141414',\n    },\n    default: {\n      50: '#E8E8E8',\n      100: '#D1D1D1',\n      200: '#A3A3A3',\n      300: '#757575',\n      400: '#474747',\n      500: '#1A1A1A',\n      600: '#141414',\n      700: '#0F0F0F',\n      800: '#0A0A0A',\n      900: '#050505',\n      DEFAULT: '#757575',\n      foreground: '#0A0A0A',\n    },\n    primary: {\n      50: '#ECF6FD',\n      100: '#DEEFFC',\n      200: '#BEDEF9',\n      300: '#9DCEF6',\n      400: '#7DBDF2',\n      500: '#5AADEF',\n      600: '#208FE9',\n      700: '#126CB5',\n      800: '#0C4879',\n      900: '#06243C',\n      DEFAULT: '#5AADEF',\n      foreground: '#2b1b1c',\n    },\n    secondary: {\n      50: '#FCEEF4',\n      100: '#F9DCE9',\n      200: '#F2B5D1',\n      300: '#EC93BB',\n      400: '#E670A5',\n      500: '#DF4C8D',\n      600: '#CB246F',\n      700: '#971B53',\n      800: '#641237',\n      900: '#34091C',\n      DEFAULT: '#DF4C8D',\n      foreground: '#fcdbe5',\n    },\n    success: {\n      50: '#E9F7EA',\n      100: '#D6F0D8',\n      200: '#A9E0AE',\n      300: '#80D188',\n      400: '#53C15E',\n      500: '#3AA145',\n      600: '#2E7F36',\n      700: '#236129',\n      800: '#17401B',\n      900: '#0C220E',\n      DEFAULT: '#3AA145',\n      foreground: '#daedda',\n    },\n    warning: {\n      50: '#FFF2E0',\n      100: '#FFE4C2',\n      200: '#FFCC8A',\n      300: '#FFB24D',\n      400: '#FF970F',\n      500: '#D37800',\n      600: '#A85F00',\n      700: '#804800',\n      800: '#573100',\n      900: '#291700',\n      DEFAULT: '#D37800',\n      foreground: '#2b1c0b',\n    },\n    danger: {\n      50: '#FBEAEA',\n      100: '#F7D5D4',\n      200: '#EEABAA',\n      300: '#E6817F',\n      400: '#DD5250',\n      500: '#D22C29',\n      600: '#A62321',\n      700: '#801B19',\n      800: '#551211',\n      900: '#2B0908',\n      DEFAULT: '#D22C29',\n      foreground: '#fbdad1',\n    },\n  },\n};\n\nconst autumnTheme = {\n  extend: 'light',\n  colors: {\n    'background-title-bar': '#fffefc',\n    'background-body': '#ffffff',\n    background: {\n      DEFAULT: '#f1f1f1',\n    },\n    foreground: {\n      DEFAULT: '#303030',\n    },\n    divider: {\n      DEFAULT: 'rgba(48, 48, 48, 0.25)',\n    },\n    focus: {\n      DEFAULT: '#64051e',\n    },\n    content1: {\n      DEFAULT: '#FDFAF4',\n      foreground: '#3E1711',\n    },\n    content2: {\n      DEFAULT: '#F8F3EA',\n      foreground: '#4B251D',\n    },\n    content3: {\n      DEFAULT: '#F2E5D6',\n      foreground: '#5D392E',\n    },\n    content4: {\n      DEFAULT: '#D9C6B3',\n      foreground: '#6F5043',\n    },\n    default: {\n      50: '#FDFAF4',\n      100: '#F8F3EA',\n      200: '#F2E5D6',\n      300: '#D9C6B3',\n      400: '#B49D8C',\n      500: '#826A5C',\n      600: '#6F5043',\n      700: '#5D392E',\n      800: '#4B251D',\n      900: '#3E1711',\n      DEFAULT: '#D9C6B3',\n      foreground: '#4B251D',\n    },\n    primary: {\n      50: '#FED7E5',\n      100: '#F8C9C7',\n      200: '#F19298',\n      300: '#D6576B',\n      400: '#AE2D4E',\n      500: '#78022C',\n      600: '#67012F',\n      700: '#560130',\n      800: '#45002D',\n      900: '#39002B',\n      DEFAULT: '#78022C',\n      foreground: '#f9b6c9',\n    },\n    secondary: {\n      50: '#F9ECED',\n      100: '#FBE2D9',\n      200: '#F8C1B4',\n      300: '#EA948A',\n      400: '#D56968',\n      500: '#B93B45',\n      600: '#9F2B3E',\n      700: '#851D38',\n      800: '#6B1231',\n      900: '#580B2D',\n      DEFAULT: '#B93B45',\n      foreground: '#fbd5d4',\n    },\n    success: {\n      50: '#EBF5F2',\n      100: '#E0F9E9',\n      200: '#C3F4D9',\n      300: '#9BDEC0',\n      400: '#76BEA4',\n      500: '#499380',\n      600: '#357E72',\n      700: '#246965',\n      800: '#175355',\n      900: '#0E3F46',\n      DEFAULT: '#499380',\n      foreground: '#ccfff0',\n    },\n    warning: {\n      50: '#FDF2E8',\n      100: '#FDF0CF',\n      200: '#FCDCA0',\n      300: '#F8C270',\n      400: '#F1A74C',\n      500: '#E97F14',\n      600: '#C8620E',\n      700: '#A7490A',\n      800: '#873306',\n      900: '#6F2403',\n      DEFAULT: '#E97F14',\n      foreground: '#301a00',\n    },\n    danger: {\n      50: '#FCE8EA',\n      100: '#FDDCD0',\n      200: '#FBB1A2',\n      300: '#F57D73',\n      400: '#EB4F51',\n      500: '#DF1A2F',\n      600: '#BF1335',\n      700: '#A00D37',\n      800: '#810836',\n      900: '#6B0435',\n      DEFAULT: '#DF1A2F',\n      foreground: '#facbd1',\n    },\n  },\n};\n\nconst businessTheme = {\n  extend: 'dark',\n  colors: {\n    'background-title-bar': '#020203',\n    'background-body': '#000000',\n    background: {\n      DEFAULT: '#212121',\n    },\n    foreground: {\n      DEFAULT: '#cccccc',\n    },\n    divider: {\n      DEFAULT: 'rgba(204, 204, 204, 0.20)',\n    },\n    focus: {\n      DEFAULT: '#1e3d65',\n    },\n    content1: {\n      DEFAULT: '#070709',\n      foreground: '#E8EAED',\n    },\n    content2: {\n      DEFAULT: '#0F1114',\n      foreground: '#CED2D9',\n    },\n    content3: {\n      DEFAULT: '#16191D',\n      foreground: '#A0A8B6',\n    },\n    content4: {\n      DEFAULT: '#1D2026',\n      foreground: '#6F7B90',\n    },\n    default: {\n      50: '#070709',\n      100: '#0F1114',\n      200: '#16191D',\n      300: '#1D2026',\n      400: '#24282F',\n      500: '#49515F',\n      600: '#6F7B90',\n      700: '#A0A8B6',\n      800: '#CED2D9',\n      900: '#E8EAED',\n      DEFAULT: '#120C12',\n      foreground: '#cdced0',\n    },\n    primary: {\n      50: '#08111B',\n      100: '#101F33',\n      200: '#182F4E',\n      300: '#204069',\n      400: '#284F82',\n      500: '#386FB7',\n      600: '#6392CF',\n      700: '#96B5DF',\n      800: '#CCDCEF',\n      900: '#E4ECF7',\n      DEFAULT: '#284F82',\n      foreground: '#d3d8e5',\n    },\n    secondary: {\n      50: '#191D1F',\n      100: '#313A3F',\n      200: '#4A575E',\n      300: '#63747E',\n      400: '#80919B',\n      500: '#98A6AE',\n      600: '#B2BCC2',\n      700: '#CCD3D7',\n      800: '#E5E9EB',\n      900: '#F4F5F6',\n      DEFAULT: '#80919B',\n      foreground: '#1d1f21',\n    },\n    success: {\n      50: '#16271B',\n      100: '#2A4B34',\n      200: '#40724F',\n      300: '#559668',\n      400: '#75B187',\n      500: '#90C09F',\n      600: '#AED1B8',\n      700: '#C8E0CF',\n      800: '#E5F0E8',\n      900: '#F2F8F4',\n      DEFAULT: '#75B187',\n      foreground: '#1b231d',\n    },\n    warning: {\n      50: '#31250C',\n      100: '#5E4817',\n      200: '#8F6D23',\n      300: '#C0922F',\n      400: '#D5AD55',\n      500: '#DEBD78',\n      600: '#E6CD99',\n      700: '#EEDDB9',\n      800: '#F7EFDE',\n      900: '#FBF7EF',\n      DEFAULT: '#D5AD55',\n      foreground: '#2a2317',\n    },\n    danger: {\n      50: '#1F0C09',\n      100: '#431914',\n      200: '#62251D',\n      300: '#813127',\n      400: '#A33E31',\n      500: '#C85647',\n      600: '#D68176',\n      700: '#E4ACA5',\n      800: '#F1D4D0',\n      900: '#F8E9E7',\n      DEFAULT: '#A33E31',\n      foreground: '#f1d9d2',\n    },\n  },\n  layout: {\n    radius: {\n      small: '0.075rem',\n      medium: '0.1rem',\n      large: '0.15rem',\n    },\n  },\n};\n\nconst acidTheme = {\n  extend: 'light',\n  colors: {\n    'background-title-bar': '#f7f7fc',\n    'background-body': '#ffffff',\n    background: {\n      DEFAULT: '#fafafa',\n    },\n    foreground: {\n      DEFAULT: '#303030',\n    },\n    divider: {\n      DEFAULT: 'rgba(48, 48, 48, 0.25)',\n    },\n    focus: {\n      DEFAULT: '#d211d2',\n    },\n    content1: {\n      DEFAULT: '#E2E2F3',\n      foreground: '#05050B',\n    },\n    content2: {\n      DEFAULT: '#C2C2E5',\n      foreground: '#0B0B19',\n    },\n    content3: {\n      DEFAULT: '#8888CD',\n      foreground: '#0F0F24',\n    },\n    content4: {\n      DEFAULT: '#4B4BB4',\n      foreground: '#151532',\n    },\n    default: {\n      50: '#E2E2F3',\n      100: '#C2C2E5',\n      200: '#8888CD',\n      300: '#4B4BB4',\n      400: '#33337A',\n      500: '#1A1A3E',\n      600: '#151532',\n      700: '#0F0F24',\n      800: '#0B0B19',\n      900: '#05050B',\n      DEFAULT: '#4B4BB4',\n      foreground: '#0B0B19',\n    },\n    primary: {\n      50: '#FEE7FE',\n      100: '#FDCEFD',\n      200: '#FBA2FB',\n      300: '#F971F9',\n      400: '#F745F7',\n      500: '#F416F5',\n      600: '#CD09CD',\n      700: '#980698',\n      800: '#670467',\n      900: '#310231',\n      DEFAULT: '#F416F5',\n      foreground: '#fcdcff',\n    },\n    secondary: {\n      50: '#FFF2E5',\n      100: '#FFE5CC',\n      200: '#FFC894',\n      300: '#FFAD61',\n      400: '#FF9029',\n      500: '#F47400',\n      600: '#C25E00',\n      700: '#944700',\n      800: '#612F00',\n      900: '#331900',\n      DEFAULT: '#F47400',\n      foreground: '#301c0b',\n    },\n    success: {\n      50: '#F0F9F4',\n      100: '#E1F4E8',\n      200: '#C4E9D1',\n      300: '#AADFBD',\n      400: '#8CD4A6',\n      500: '#6FC990',\n      600: '#44B66E',\n      700: '#338953',\n      800: '#215936',\n      900: '#112D1B',\n      DEFAULT: '#6FC990',\n      foreground: '#1b2920',\n    },\n    warning: {\n      50: '#FEFCE7',\n      100: '#FDF9D3',\n      200: '#FBF2A3',\n      300: '#F8EB77',\n      400: '#F6E446',\n      500: '#F4DF1A',\n      600: '#CCB90A',\n      700: '#9C8D08',\n      800: '#665C05',\n      900: '#363003',\n      DEFAULT: '#F4DF1A',\n      foreground: '#2e2a13',\n    },\n    danger: {\n      50: '#FEE3E1',\n      100: '#FECBC8',\n      200: '#FC928C',\n      300: '#FB5E56',\n      400: '#FA2A1F',\n      500: '#DA1005',\n      600: '#AE0D04',\n      700: '#820903',\n      800: '#550602',\n      900: '#2D0301',\n      DEFAULT: '#DA1005',\n      foreground: '#fbd9cc',\n    },\n  },\n};\n\nconst lemonadeTheme = {\n  extend: 'light',\n  colors: {\n    'background-title-bar': '#f7f7fc',\n    'background-body': '#ffffff',\n    background: {\n      DEFAULT: '#ffffff',\n    },\n    foreground: {\n      DEFAULT: '#303030',\n    },\n    divider: {\n      DEFAULT: 'rgba(48, 48, 48, 0.25)',\n    },\n    focus: {\n      DEFAULT: '#477800',\n    },\n    content1: {\n      DEFAULT: '#E2E2F3',\n      foreground: '#05050B',\n    },\n    content2: {\n      DEFAULT: '#C2C2E5',\n      foreground: '#0B0B19',\n    },\n    content3: {\n      DEFAULT: '#8888CD',\n      foreground: '#0F0F24',\n    },\n    content4: {\n      DEFAULT: '#4B4BB4',\n      foreground: '#151532',\n    },\n    default: {\n      50: '#E2E2F3',\n      100: '#C2C2E5',\n      200: '#8888CD',\n      300: '#4B4BB4',\n      400: '#33337A',\n      500: '#1A1A3E',\n      600: '#151532',\n      700: '#0F0F24',\n      800: '#0B0B19',\n      900: '#05050B',\n      DEFAULT: '#4B4BB4',\n      foreground: '#0B0B19',\n    },\n    primary: {\n      50: '#F0FFDB',\n      100: '#E1FFB8',\n      200: '#C3FF70',\n      300: '#A6FF29',\n      400: '#83E000',\n      500: '#5B9A00',\n      600: '#477A00',\n      700: '#365C00',\n      800: '#243D00',\n      900: '#121F00',\n      DEFAULT: '#5B9A00',\n      foreground: '#dfebd0',\n    },\n    secondary: {\n      50: '#FDFDE8',\n      100: '#FAFAD1',\n      200: '#F6F6A7',\n      300: '#F2F278',\n      400: '#EDED4A',\n      500: '#E8E91E',\n      600: '#BEBE13',\n      700: '#90900E',\n      800: '#62620A',\n      900: '#2E2E05',\n      DEFAULT: '#E8E91E',\n      foreground: '#2e2d13',\n    },\n    success: {\n      50: '#FCFEF6',\n      100: '#F9FDED',\n      200: '#F2FAD7',\n      300: '#ECF7C5',\n      400: '#E6F5B3',\n      500: '#E0F29E',\n      600: '#C9E959',\n      700: '#ACD31C',\n      800: '#718B13',\n      900: '#3A480A',\n      DEFAULT: '#E0F29E',\n      foreground: '#2c2f22',\n    },\n    warning: {\n      50: '#FEFCF1',\n      100: '#FDFAE8',\n      200: '#FBF4D0',\n      300: '#F8EEB4',\n      400: '#F6E99D',\n      500: '#F4E485',\n      600: '#EED43F',\n      700: '#CEB213',\n      800: '#8C790D',\n      900: '#463D06',\n      DEFAULT: '#F4E485',\n      foreground: '#2e2b1e',\n    },\n    danger: {\n      50: '#FDF7F7',\n      100: '#FBEFEF',\n      200: '#F8E3E2',\n      300: '#F4D3D2',\n      400: '#F1C7C6',\n      500: '#EDB7B5',\n      600: '#DD7773',\n      700: '#C93631',\n      800: '#872421',\n      900: '#421210',\n      DEFAULT: '#EDB7B5',\n      foreground: '#2c2524',\n    },\n  },\n};\n\nconst nightTheme = {\n  extend: 'dark',\n  colors: {\n    'background-title-bar': '#020308',\n    'background-body': '#000000',\n    background: {\n      DEFAULT: '#101729',\n    },\n    foreground: {\n      DEFAULT: '#b7c6f0',\n    },\n    divider: {\n      DEFAULT: 'rgba(183, 198, 240, 0.20)',\n    },\n    focus: {\n      DEFAULT: '#47b4f8',\n    },\n    content1: {\n      DEFAULT: '#050A1C',\n      foreground: '#EDF6FC',\n    },\n    content2: {\n      DEFAULT: '#091022',\n      foreground: '#DDE9F5',\n    },\n    content3: {\n      DEFAULT: '#0F172A',\n      foreground: '#BDD4EB',\n    },\n    content4: {\n      DEFAULT: '#151F32',\n      foreground: '#8AA4C4',\n    },\n    default: {\n      50: '#050A1C',\n      100: '#091022',\n      200: '#0F172A',\n      300: '#151F32',\n      400: '#1E293B',\n      500: '#566B89',\n      600: '#8AA4C4',\n      700: '#BDD4EB',\n      800: '#DDE9F5',\n      900: '#EDF6FC',\n      DEFAULT: '#1E293B',\n      foreground: '#b9cdf5',\n    },\n    primary: {\n      50: '#072B66',\n      100: '#0C3D7B',\n      200: '#145799',\n      300: '#1D73B7',\n      400: '#2894D5',\n      500: '#59B9E5',\n      600: '#7CD4F2',\n      700: '#A9EBFA',\n      800: '#D3F7FC',\n      900: '#E3EFFB',\n      DEFAULT: '#2894D5',\n      foreground: '#092a3d',\n    },\n    secondary: {\n      50: '#121666',\n      100: '#1D237B',\n      200: '#2F3699',\n      300: '#444DB7',\n      400: '#5E68D5',\n      500: '#858EE5',\n      600: '#A1A9F2',\n      700: '#C2C8FA',\n      800: '#E0E4FC',\n      900: '#EFF0FB',\n      DEFAULT: '#5E68D5',\n      foreground: '#04084c',\n    },\n    success: {\n      50: '#084F65',\n      100: '#0E6A7A',\n      200: '#169098',\n      300: '#20B6B1',\n      400: '#2DD4BF',\n      500: '#5DE5C7',\n      600: '#7FF2CE',\n      700: '#ABFADA',\n      800: '#D5FCE9',\n      900: '#EAFBF8',\n      DEFAULT: '#2DD4BF',\n      foreground: '#0c332d',\n    },\n    warning: {\n      50: '#75460F',\n      100: '#8D5C19',\n      200: '#AF7A28',\n      300: '#D19B3A',\n      400: '#F4BF50',\n      500: '#F8D47B',\n      600: '#FBE196',\n      700: '#FDEEB9',\n      800: '#FEF7DC',\n      900: '#FEF8EC',\n      DEFAULT: '#F4BF50',\n      foreground: '#3f2c00',\n    },\n    danger: {\n      50: '#781549',\n      100: '#912353',\n      200: '#B43862',\n      300: '#D75172',\n      400: '#FB7085',\n      500: '#FC9399',\n      600: '#FDACA9',\n      700: '#FECDC6',\n      800: '#FEE8E2',\n      900: '#FFF0F2',\n      DEFAULT: '#FB7085',\n      foreground: '#45010c',\n    },\n  },\n};\n\nconst coffeeTheme = {\n  extend: 'dark',\n  colors: {\n    'background-title-bar': '#010101',\n    'background-body': '#000000',\n    background: {\n      DEFAULT: '#211721',\n    },\n    foreground: {\n      DEFAULT: '#736d62',\n    },\n    divider: {\n      DEFAULT: 'rgba(115, 109, 98, 0.20)',\n    },\n    focus: {\n      DEFAULT: '#ce822b',\n    },\n    content1: {\n      DEFAULT: '#030203',\n      foreground: '#EDE3ED',\n    },\n    content2: {\n      DEFAULT: '#060406',\n      foreground: '#D8C5D8',\n    },\n    content3: {\n      DEFAULT: '#0C080C',\n      foreground: '#B18BB1',\n    },\n    content4: {\n      DEFAULT: '#0F0A0F',\n      foreground: '#875A87',\n    },\n    default: {\n      50: '#030203',\n      100: '#060406',\n      200: '#0C080C',\n      300: '#0F0A0F',\n      400: '#120C12',\n      500: '#4D334D',\n      600: '#875A87',\n      700: '#B18BB1',\n      800: '#D8C5D8',\n      900: '#EDE3ED',\n      DEFAULT: '#120C12',\n      foreground: '#cac9ca',\n    },\n    primary: {\n      50: '#2E1D0B',\n      100: '#5F3D16',\n      200: '#8D5A21',\n      300: '#BA782B',\n      400: '#D5944A',\n      500: '#DDA96E',\n      600: '#E6BF93',\n      700: '#EFD5B9',\n      800: '#F6E9DA',\n      900: '#FBF5EE',\n      DEFAULT: '#D5944A',\n      foreground: '#2c2015',\n    },\n    secondary: {\n      50: '#080C0C',\n      100: '#101819',\n      200: '#182425',\n      300: '#223334',\n      400: '#2A3F40',\n      500: '#4B7072',\n      600: '#6B9C9E',\n      700: '#9CBDBE',\n      800: '#CEDEDF',\n      900: '#E6EEEF',\n      DEFAULT: '#2A3F40',\n      foreground: '#d2d6d6',\n    },\n    success: {\n      50: '#1F2617',\n      100: '#405030',\n      200: '#5F7647',\n      300: '#7E9C5E',\n      400: '#9EB685',\n      500: '#B1C49C',\n      600: '#C5D3B6',\n      700: '#D9E2CF',\n      800: '#EBF0E5',\n      900: '#F5F7F2',\n      DEFAULT: '#9EB685',\n      foreground: '#21251d',\n    },\n    warning: {\n      50: '#403102',\n      100: '#866504',\n      200: '#C69606',\n      300: '#F8C11B',\n      400: '#FAD25D',\n      500: '#FBDC7E',\n      600: '#FCE49C',\n      700: '#FDEDBF',\n      800: '#FEF6DC',\n      900: '#FFFBF0',\n      DEFAULT: '#FAD25D',\n      foreground: '#2f2818',\n    },\n    danger: {\n      50: '#461207',\n      100: '#87230D',\n      200: '#CD3614',\n      300: '#ED6040',\n      400: '#F39883',\n      500: '#F6AE9D',\n      600: '#F8C1B5',\n      700: '#FAD4CC',\n      800: '#FDECE8',\n      900: '#FEF3F1',\n      DEFAULT: '#F39883',\n      foreground: '#2e201d',\n    },\n  },\n};\n\nconst winterTheme = {\n  extend: 'light',\n  colors: {\n    'background-title-bar': '#f3f7fe',\n    'background-body': '#ffffff',\n    background: {\n      DEFAULT: '#ffffff',\n    },\n    foreground: {\n      DEFAULT: '#3c4e6b',\n    },\n    divider: {\n      DEFAULT: 'rgba(60, 78, 107, 0.25)',\n    },\n    focus: {\n      DEFAULT: '#2a69e2',\n    },\n    content1: {\n      DEFAULT: '#D5E2FB',\n      foreground: '#010409',\n    },\n    content2: {\n      DEFAULT: '#ACC5F7',\n      foreground: '#020713',\n    },\n    content3: {\n      DEFAULT: '#588AEE',\n      foreground: '#030B1C',\n    },\n    content4: {\n      DEFAULT: '#1655D5',\n      foreground: '#040F25',\n    },\n    default: {\n      50: '#D5E2FB',\n      100: '#ACC5F7',\n      200: '#588AEE',\n      300: '#1655D5',\n      400: '#0D3482',\n      500: '#051431',\n      600: '#040F25',\n      700: '#030B1C',\n      800: '#020713',\n      900: '#010409',\n      DEFAULT: '#1655D5',\n      foreground: '#020713',\n    },\n    primary: {\n      50: '#EBF2FF',\n      100: '#D6E4FF',\n      200: '#ADCAFF',\n      300: '#85AFFF',\n      400: '#5C95FF',\n      500: '#337AFF',\n      600: '#0056F5',\n      700: '#0040B8',\n      800: '#002B7A',\n      900: '#00153D',\n      DEFAULT: '#337AFF',\n      foreground: '#dce1ff',\n    },\n    secondary: {\n      50: '#EAE9F7',\n      100: '#D9D6F0',\n      200: '#AFA9E0',\n      300: '#8980D1',\n      400: '#6357C2',\n      500: '#473BA2',\n      600: '#393083',\n      700: '#2B2361',\n      800: '#1C1740',\n      900: '#0F0C22',\n      DEFAULT: '#473BA2',\n      foreground: '#dad4ed',\n    },\n    success: {\n      50: '#F4FAFB',\n      100: '#E9F6F6',\n      200: '#D0EBEC',\n      300: '#BAE1E3',\n      400: '#A4D8DB',\n      500: '#8BCED1',\n      600: '#5BB9BE',\n      700: '#3B9196',\n      800: '#276063',\n      900: '#143233',\n      DEFAULT: '#8BCED1',\n      foreground: '#1f2929',\n    },\n    warning: {\n      50: '#FDFAF7',\n      100: '#FCF8F3',\n      200: '#F7EFE3',\n      300: '#F4E7D7',\n      400: '#F1E0CB',\n      500: '#EDD8BC',\n      600: '#DBB17A',\n      700: '#C98936',\n      800: '#855B24',\n      900: '#442F12',\n      DEFAULT: '#EDD8BC',\n      foreground: '#2c2925',\n    },\n    danger: {\n      50: '#FCF3F3',\n      100: '#F8E7E7',\n      200: '#F1D0D0',\n      300: '#EAB8B8',\n      400: '#E4A4A4',\n      500: '#DD8C8C',\n      600: '#CD5656',\n      700: '#A53131',\n      800: '#6E2121',\n      900: '#371010',\n      DEFAULT: '#DD8C8C',\n      foreground: '#2c201f',\n    },\n  },\n};\n\nconst themesConfig = {\n  light: lightBase, // blue\n  dark: darkBase, // blue\n  'light-red': lightRedTheme,\n  'light-yellow': lightYellowTheme,\n  'light-green': lightGreenTheme,\n  'light-cyan': lightCyanTheme,\n  'light-purple': lightPurpleTheme,\n  'light-pink': lightPinkTheme,\n  'dark-red': darkRedTheme,\n  'dark-yellow': darkYellowTheme,\n  'dark-green': darkGreenTheme,\n  'dark-cyan': darkCyanTheme,\n  'dark-purple': darkPurpleTheme,\n  'dark-pink': darkPinkTheme,\n  cupcake: cupcakeTheme,\n  bumblebee: bumblebeeTheme,\n  emerald: emeraldTheme,\n  corporate: corporateTheme,\n  synthwave: synthwaveTheme,\n  retro: retroTheme,\n  cyberpunk: cyberpunkTheme,\n  valentine: valentineTheme,\n  halloween: halloweenTheme,\n  garden: gardenTheme,\n  forest: forestTheme,\n  aqua: aquaTheme,\n  lofi: lofiTheme,\n  pastel: pastelTheme,\n  fantasy: fantasyTheme,\n  wireframe: wireframeTheme,\n  luxury: luxuryTheme,\n  dracula: draculaTheme,\n  cmyk: cmykTheme,\n  autumn: autumnTheme,\n  business: businessTheme,\n  acid: acidTheme,\n  lemonade: lemonadeTheme,\n  night: nightTheme,\n  coffee: coffeeTheme,\n  winter: winterTheme,\n};\n\nexport default themesConfig;\n"
  },
  {
    "path": "app/types/handle.ts",
    "content": "import type { SEOHandle } from '@nasa-gcn/remix-seo';\nimport type { Params, UIMatch } from '@remix-run/react';\nimport type { TFunction } from 'i18next';\n\nexport declare type Handle = {\n  breadcrumb?: (options: {\n    match: UIMatch<any>;\n    t: TFunction<'translation', undefined>;\n    params: Readonly<Params<string>>;\n  }) => React.ReactNode;\n  miniTitle?: (options: {\n    match: UIMatch;\n    parentMatch?: UIMatch;\n    t: TFunction<'translation', undefined>;\n    params: Readonly<Params<string>>;\n  }) => {\n    title: string;\n    subtitle?: string;\n    showImage?: boolean;\n    imageUrl?: string;\n  };\n  showTabLink?: boolean;\n  i18n?: string;\n  disableLayoutPadding?: boolean;\n  tabLinkPages?:\n    | ((options: { params: Readonly<Params<string>> }) => {\n        pageName: string;\n        pageLink: string;\n      }[])\n    | {\n        pageName: string;\n        pageLink: string;\n      }[];\n  tabLinkTo?: (options: { params: Readonly<Params<string>> }) => string;\n  hideTabLinkWithLocation?: (locationPathname: string) => boolean;\n  playerSettings?: {\n    isMini?: boolean;\n    shouldShowPlayer?: boolean;\n  };\n  preventScrollToTop?: boolean;\n  customHeaderBackgroundColor?: boolean;\n  customHeaderChangeColorOnScroll?: boolean;\n  showListViewChangeButton?: boolean;\n  hideSidebar?: boolean;\n  hideMobileHeader?: boolean;\n} & SEOHandle;\n"
  },
  {
    "path": "app/types/media.ts",
    "content": "export interface IMedia {\n  adult?: boolean;\n  backdropPath?: string;\n  castId?: number;\n  character?: string;\n  color?: string;\n  creditId?: string;\n  department?: string;\n  duration?: number;\n  episodeId?: string;\n  episodeNumber?: number;\n  episodes?: number;\n  episodeTitle?: string;\n  firstAirDate?: string;\n  gender?: number | null;\n  genreIds?: number[];\n  genresAnime?: string[];\n  id?: number | string;\n  job?: string;\n  knownFor?: IMedia[];\n  knownForDepartment?: string;\n  malId?: number;\n  mediaType: 'movie' | 'tv' | 'anime' | 'people';\n  name?: string;\n  order?: number;\n  originalCountry?: string;\n  originalLanguage?: string;\n  originalName?: string;\n  originalTitle?: string;\n  overview?: string;\n  popularity?: number;\n  posterPath?: string;\n  rating?: number;\n  relationType?: string;\n  releaseDate?: string | number;\n  status?: string;\n  title?: string | Title;\n  totalEpisodes?: number;\n  trailer?: ITrailer;\n  type?: Type;\n  video?: boolean;\n  voteAverage?: number;\n  voteCount?: number;\n}\n\nexport interface ITrailer {\n  id?: string;\n  site?: string;\n  thumbnail?: string;\n}\n\nexport interface Title {\n  english?: null | string;\n  native?: string;\n  romaji?: string;\n  userPreferred?: string;\n}\n\nexport enum Type {\n  Movie = 'MOVIE',\n  Music = 'MUSIC',\n  Ona = 'ONA',\n  Ova = 'OVA',\n  Special = 'SPECIAL',\n  Tv = 'TV',\n  TvShort = 'TV_SHORT',\n}\n"
  },
  {
    "path": "app/utils/client/gtags.client.ts",
    "content": "declare global {\n  interface Window {\n    gtag: (option: string, gaTrackingId: string, options: Record<string, unknown>) => void;\n  }\n}\n\n/**\n * @example\n * https://developers.google.com/analytics/devguides/collection/gtagjs/pages\n */\nexport const pageview = (url: string, trackingId: string) => {\n  if (!window.gtag) {\n    console.warn(\n      'window.gtag is not defined. This could mean your google analytics script has not loaded on the page yet.',\n    );\n    return;\n  }\n  window.gtag('config', trackingId, {\n    page_path: url,\n  });\n};\n\n/**\n * @example\n * https://developers.google.com/analytics/devguides/collection/gtagjs/events\n */\nexport const event = ({ action, category, label, value }: Record<string, string>) => {\n  if (!window.gtag) {\n    console.warn(\n      'window.gtag is not defined. This could mean your google analytics script has not loaded on the page yet.',\n    );\n    return;\n  }\n  window.gtag('event', action, {\n    event_category: category,\n    event_label: label,\n    value,\n  });\n};\n"
  },
  {
    "path": "app/utils/client/meta-tags.client.ts",
    "content": "export function setMetaThemeColor(color: string) {\n  const metaThemeColor = document.querySelector('meta[name=theme-color]');\n  if (metaThemeColor) {\n    metaThemeColor.setAttribute('content', color);\n  }\n}\n\nexport function getBackgroundTitleBarColor(isHydrated: boolean) {\n  if (isHydrated) {\n    return window\n      .getComputedStyle(document.documentElement)\n      .getPropertyValue('--theme-background-title-bar');\n  }\n  return 'hsl(0, 0%, 0%)';\n}\n"
  },
  {
    "path": "app/utils/client/update-history.ts",
    "content": "import type { FetcherWithComponents } from '@remix-run/react';\nimport type Artplayer from 'artplayer';\n\nexport default function updateHistory(\n  art: Artplayer,\n  fetcher: FetcherWithComponents<unknown>,\n  id: string,\n  route: string,\n  media_type: 'movie' | 'tv' | 'anime',\n  title: string,\n  overview: string,\n  season?: string,\n  episode?: string,\n) {\n  let played = false;\n\n  art.on('play', () => {\n    played = true;\n  });\n\n  art.on('destroy', () => {\n    if (played) {\n      fetcher.submit(\n        {\n          user_id: id,\n          duration: art.duration.toString(),\n          watched: art.currentTime.toString(),\n          route,\n          media_type,\n          poster: art.poster,\n          title,\n          overview,\n          season: season ?? '',\n          episode: episode ?? '',\n        },\n        { method: 'post', action: '/api/history' },\n      );\n    }\n  });\n}\n"
  },
  {
    "path": "app/utils/encode.ts",
    "content": "const encode = (str: string) =>\n  `aaa${str\n    .match(/.{1,5}/g)\n    ?.map((s, i) => btoa(unescape(encodeURIComponent(s))).replace(/=/g, i % 2 ? '!' : ''))\n    .reverse()\n    .join('')\n    .padEnd(40, '!')\n    .replace(/!/g, 'a')\n    .slice(3)}`;\n\nexport default encode;\n"
  },
  {
    "path": "app/utils/file.ts",
    "content": "export function getExt(url: string): string | undefined {\n  if (url.includes('?')) {\n    return getExt(url.split('?')[0]);\n  }\n\n  if (url.includes('#')) {\n    return getExt(url.split('#')[0]);\n  }\n\n  return url.trim().toLowerCase().split('.').pop();\n}\n"
  },
  {
    "path": "app/utils/function.ts",
    "content": "type ThrottleFunction<T extends unknown[], R> = (...args: T) => R;\n\nfunction throttle<T extends unknown[], R>(\n  fn: ThrottleFunction<T, R>,\n  delay: number,\n): ThrottleFunction<T, R> {\n  let timerId: ReturnType<typeof setTimeout> | undefined;\n  let lastArgs: T | undefined;\n\n  return function throttledFn(...args: T): R {\n    lastArgs = args;\n    if (!timerId) {\n      timerId = setTimeout(() => {\n        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n        fn(...lastArgs!);\n        timerId = undefined;\n        lastArgs = undefined;\n      }, delay);\n    }\n    return {} as R;\n  };\n}\n\nexport { throttle };\n"
  },
  {
    "path": "app/utils/index.ts",
    "content": "export * from './function';\nexport * from './object';\nexport * from './encode';\nexport * from './file';\nexport * from './media';\nexport * from './misc';\nexport * from './merge-meta';\n"
  },
  {
    "path": "app/utils/media.ts",
    "content": "/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable @typescript-eslint/indent */\n/* eslint-disable no-bitwise */\nimport { type IMedia } from '~/types/media';\n\nexport type PosterSize = 'w92' | 'w154' | 'w185' | 'w342' | 'w500' | 'w780' | 'original';\nexport type BackdropSize = 'w300' | 'w780' | 'w1280' | 'original';\nexport type LogoSize = 'w45' | 'w92' | 'w154' | 'w185' | 'w300' | 'w500' | 'original';\nexport type ProfileSize = 'w45' | 'w185' | 'h632' | 'original';\nexport type StillSize = 'w92' | 'w185' | 'w300' | 'original';\n\n/* TMDB is a class that has two static methods, posterUrl and backdropUrl, that return a string. */\nexport default class TMDB {\n  static readonly media_base_url = 'https://image.tmdb.org/t/p/';\n\n  static posterUrl = (path: string | undefined, size?: PosterSize): string => {\n    if (size) {\n      return `${this.media_base_url}${size}/${path}`;\n    }\n    return `${this.media_base_url}original/${path}`;\n  };\n\n  static backdropUrl = (path: string | undefined, size?: BackdropSize): string => {\n    if (size) {\n      return `${this.media_base_url}${size}/${path}`;\n    }\n    return `${this.media_base_url}original/${path}`;\n  };\n\n  static profileUrl = (path: string | undefined, size?: ProfileSize): string => {\n    if (size) {\n      return `${this.media_base_url}${size}/${path}`;\n    }\n    return `${this.media_base_url}original/${path}`;\n  };\n\n  static logoUrl = (path: string | undefined, size?: LogoSize): string => {\n    if (size) {\n      return `${this.media_base_url}${size}/${path}`;\n    }\n    return `${this.media_base_url}original/${path}`;\n  };\n\n  static postFetchDataHandler = (data: any): IMedia[] => {\n    const result: IMedia[] = [];\n\n    const transform = (item: any, mediaType?: 'movie' | 'tv'): IMedia => ({\n      id: item.id,\n      title: item.title || item.name,\n      overview: item.overview,\n      posterPath: item.poster_path ? TMDB.posterUrl(item.poster_path, 'w342') : undefined,\n      backdropPath: item.backdrop_path ? TMDB.backdropUrl(item.backdrop_path, 'w780') : undefined,\n      releaseDate: item.release_date || item.first_air_date,\n      voteAverage: item.vote_average,\n      voteCount: item.vote_count,\n      mediaType: mediaType ?? item.media_type,\n      popularity: item.popularity,\n      originalLanguage: item.original_language,\n      genreIds: item.genre_ids,\n      originalTitle: item.original_title || item.original_name,\n    });\n\n    if (Array.isArray(data)) {\n      data.forEach((item: any) => result.push(transform(item, item.media_type)));\n    } else {\n      result.push(transform(data));\n    }\n\n    return result;\n  };\n}\n"
  },
  {
    "path": "app/utils/merge-meta.ts",
    "content": "// Meta merging helper for overriding and appending meta tags\n// https://gist.github.com/ryanflorence/ec1849c6d690cfbffcb408ecd633e069\n\nimport type { LoaderFunction, MetaDescriptor, MetaFunction } from '@remix-run/node';\n\nexport function mergeMeta<\n  Loader extends LoaderFunction | unknown = unknown,\n  ParentsLoaders extends Record<string, LoaderFunction> = {},\n>(\n  overrideFn: MetaFunction<Loader, ParentsLoaders>,\n  appendFn?: MetaFunction<Loader, ParentsLoaders>,\n): MetaFunction<Loader, ParentsLoaders> {\n  return (arg) => {\n    // get meta and overwrite meta title, name, property from parent routes\n    let mergedMeta = arg.matches.reduce<MetaDescriptor[]>((acc, match) => {\n      (match as { meta?: MetaDescriptor[] }).meta?.forEach((meta) => {\n        const index = acc.findIndex((m) => {\n          return (\n            ('name' in m && 'name' in meta && m.name === meta.name) ||\n            ('property' in m && 'property' in meta && m.property === meta.property) ||\n            ('title' in m && 'title' in meta)\n          );\n        });\n\n        if (index > -1) {\n          acc[index] = meta;\n        } else {\n          acc.push(meta);\n        }\n      });\n      return acc;\n    }, []);\n\n    // replace any parent meta with the same name or property with the override\n    const overrides = overrideFn(arg);\n\n    overrides.forEach((override) => {\n      const index = mergedMeta.findIndex((meta) => {\n        return (\n          ('name' in meta && 'name' in override && meta.name === override.name) ||\n          ('property' in meta && 'property' in override && meta.property === override.property) ||\n          ('title' in meta && 'title' in override)\n        );\n      });\n\n      if (index > -1) {\n        mergedMeta[index] = override;\n      } else {\n        mergedMeta.push(override);\n      }\n    });\n\n    // append any additional meta\n    if (appendFn) {\n      mergedMeta = mergedMeta.concat(appendFn(arg));\n    }\n\n    return mergedMeta;\n  };\n}\n"
  },
  {
    "path": "app/utils/misc.ts",
    "content": "import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n\nexport function getErrorMessage(error: unknown) {\n  if (typeof error === 'string') return error;\n  if (\n    error &&\n    typeof error === 'object' &&\n    'message' in error &&\n    typeof error.message === 'string'\n  ) {\n    return error.message;\n  }\n  console.error('Unable to get error message for error', error);\n  return 'Unknown Error';\n}\n\nexport function getDomainUrl(request: Request) {\n  const host = request.headers.get('X-Forwarded-Host') ?? request.headers.get('host');\n  if (!host) {\n    throw new Error('Could not determine domain URL.');\n  }\n  const protocol = host.includes('localhost') ? 'http' : 'https';\n  return `${protocol}://${host}`;\n}\n\n/**\n * Merge multiple headers objects into one (uses set so headers are overridden)\n */\nexport function mergeHeaders(...headers: Array<Headers>) {\n  const merged = new Headers();\n  for (const header of headers) {\n    for (const [key, value] of header.entries()) {\n      merged.set(key, value);\n    }\n  }\n  return merged;\n}\n\n/**\n * Combine multiple header objects into one (uses append so headers are not overridden)\n */\nexport function combineHeaders(...headers: Array<Headers>) {\n  const combined = new Headers();\n  for (const header of headers) {\n    for (const [key, value] of header.entries()) {\n      combined.append(key, value);\n    }\n  }\n  return combined;\n}\n\nexport function errorBlock(err: unknown) {\n  if (typeof err === 'string') {\n    return {\n      ok: false,\n      message: err,\n    };\n  } else if (err instanceof Error) {\n    return {\n      ok: false,\n      message: err.message,\n    };\n  } else {\n    return {\n      ok: false,\n      message: 'Unknown error',\n    };\n  }\n}\n\nexport const shareSupported = () => {\n  try {\n    return 'share' in navigator;\n  } catch (error) {\n    return errorBlock(error);\n  }\n};\n"
  },
  {
    "path": "app/utils/object.ts",
    "content": "export function swapColorValues<T extends Record<string, string>>(colors: T) {\n  const swappedColors = {};\n  const keys = Object.keys(colors);\n  const length = keys.length;\n\n  for (let i = 0; i < length / 2; i++) {\n    const key1 = keys[i];\n    const key2 = keys[length - 1 - i];\n\n    // @ts-ignore\n    swappedColors[key1] = colors[key2];\n    // @ts-ignore\n    swappedColors[key2] = colors[key1];\n  }\n  if (length % 2 !== 0) {\n    const middleKey = keys[Math.floor(length / 2)];\n\n    // @ts-ignore\n    swappedColors[middleKey] = colors[middleKey];\n  }\n\n  return swappedColors;\n}\n"
  },
  {
    "path": "app/utils/react/ClientOnly.tsx",
    "content": "import * as React from 'react';\n\nimport { useHydrated } from '~/utils/react/hooks/useHydrated';\n\ntype Props = {\n  /**\n   * You are encouraged to add a fallback that is the same dimensions\n   * as the client rendered children. This will avoid content layout\n   * shift which is disgusting\n   */\n  children(): React.ReactNode;\n  fallback?: React.ReactNode;\n};\n\n/**\n * Render the children only after the JS has loaded client-side. Use an optional\n * fallback component if the JS is not yet loaded.\n *\n * Example: Render a Chart component if JS loads, renders a simple FakeChart\n * component server-side or if there is no JS. The FakeChart can have only the\n * UI without the behavior or be a loading spinner or skeleton.\n * ```tsx\n * return (\n *   <ClientOnly fallback={<FakeChart />}>\n *     {() => <Chart />}\n *   </ClientOnly>\n * );\n * ```\n */\nexport function ClientOnly({ children, fallback = null }: Props) {\n  return useHydrated() ? <>{children()}</> : <>{fallback}</>;\n}\n"
  },
  {
    "path": "app/utils/react/hooks/useColorDarkenLighten.ts",
    "content": "import { useMemo } from 'react';\nimport { useTheme } from 'next-themes';\nimport tinycolor from 'tinycolor2';\n\nexport default function useColorDarkenLighten(color?: string) {\n  const { theme } = useTheme();\n  const isDark = useMemo(() => {\n    const darkTheme = [\n      'dark',\n      'synthwave',\n      'dracula',\n      'night',\n      'halloween',\n      'forest',\n      'business',\n      'coffee',\n      'luxury',\n    ];\n    if (theme) {\n      if (theme === 'system') {\n        return window.matchMedia('(prefers-color-scheme: dark)').matches;\n      }\n      return darkTheme.includes(theme) || theme.includes('dark');\n    }\n    return false;\n  }, [theme]);\n  const brightnessColor = (tinycolor(color).getBrightness() / 255) * 100;\n  const darkenLightenColor = useMemo(() => {\n    if (isDark) {\n      return brightnessColor > 70\n        ? tinycolor(color)\n            .darken(brightnessColor - 70)\n            .toString()\n        : tinycolor(color)\n            .lighten(70 - brightnessColor)\n            .toString();\n    }\n    return brightnessColor > 30\n      ? tinycolor(color)\n          .darken(brightnessColor - 30)\n          .toString()\n      : tinycolor(color)\n          .lighten(30 - brightnessColor)\n          .toString();\n  }, [brightnessColor, color, isDark]);\n\n  const backgroundColor = useMemo(() => {\n    if (isDark) {\n      return brightnessColor > 30\n        ? tinycolor(color)\n            .darken(brightnessColor - 30)\n            .toString()\n        : tinycolor(color)\n            .lighten(30 - brightnessColor)\n            .toString();\n    }\n    return brightnessColor > 70\n      ? tinycolor(color)\n          .darken(brightnessColor - 70)\n          .toString()\n      : tinycolor(color)\n          .lighten(70 - brightnessColor)\n          .toString();\n  }, [brightnessColor, color, isDark]);\n\n  const backgroundInvertColor = useMemo(() => {\n    if (isDark) {\n      return brightnessColor > 30\n        ? tinycolor(color)\n            .darken(brightnessColor - 30)\n            .spin(180)\n            .toString()\n        : tinycolor(color)\n            .lighten(30 - brightnessColor)\n            .spin(180)\n            .toString();\n    }\n    return brightnessColor > 70\n      ? tinycolor(color)\n          .darken(brightnessColor - 70)\n          .spin(180)\n          .toString()\n      : tinycolor(color)\n          .lighten(70 - brightnessColor)\n          .spin(180)\n          .toString();\n  }, [brightnessColor, color, isDark]);\n\n  const saturatedColor = useMemo(() => {\n    if (isDark) {\n      return brightnessColor > 70\n        ? tinycolor(color)\n            .darken(brightnessColor - 70)\n            .saturate(70)\n            .toString()\n        : tinycolor(color)\n            .lighten(70 - brightnessColor)\n            .saturate(70)\n            .toString();\n    }\n    return brightnessColor > 30\n      ? tinycolor(color)\n          .darken(brightnessColor - 30)\n          .saturate(70)\n          .toString()\n      : tinycolor(color)\n          .lighten(30 - brightnessColor)\n          .saturate(70)\n          .toString();\n  }, [brightnessColor, color, isDark]);\n\n  const invertColor = useMemo(() => {\n    if (isDark) {\n      return brightnessColor > 70\n        ? tinycolor(color)\n            .darken(brightnessColor - 70)\n            .saturate(70)\n            .spin(180)\n            .toString()\n        : tinycolor(color)\n            .lighten(70 - brightnessColor)\n            .saturate(70)\n            .spin(180)\n            .toString();\n    }\n    return brightnessColor > 30\n      ? tinycolor(color)\n          .darken(brightnessColor - 30)\n          .saturate(70)\n          .spin(180)\n          .toString()\n      : tinycolor(color)\n          .lighten(30 - brightnessColor)\n          .saturate(70)\n          .spin(180)\n          .toString();\n  }, [brightnessColor, color, isDark]);\n\n  return {\n    isDark,\n    darkenLightenColor,\n    backgroundColor,\n    backgroundInvertColor,\n    saturatedColor,\n    invertColor,\n  };\n}\n"
  },
  {
    "path": "app/utils/react/hooks/useGlobalNavigationState.ts",
    "content": "import { useMemo } from 'react';\nimport { useFetchers, useNavigation, useRevalidator } from '@remix-run/react';\n\n/**\n * This is a helper hook that returns the state of every fetcher active on\n * the app and combine it with the state of the global transition and\n * revalidator.\n * @example\n * const states = useGlobalNavigationState();\n * if (state.includes(\"loading\")) {\n *   // The app is loading or revalidating.\n * }\n * if (state.includes(\"submitting\")) {\n *   // The app is submitting.\n * }\n * // The app is idle\n */\nexport function useGlobalNavigationState() {\n  const { state: navigationState } = useNavigation();\n  const { state: revalidatorState } = useRevalidator();\n  const fetchers = useFetchers();\n\n  /**\n   * This gets the state of every fetcher active on the app and combine it with\n   * the state of the global transition (Link and Form) and revalidator.\n   */\n  return useMemo(\n    function getGlobalNavigationState() {\n      return [\n        navigationState,\n        // The type cast here is used to remove RevalidatorState from the union\n        revalidatorState as 'idle' | 'loading',\n        ...fetchers.map((fetcher) => fetcher.state),\n      ];\n    },\n    [navigationState, revalidatorState, fetchers],\n  );\n}\n\n/**\n * const you know if the app is pending some request, either global transition\n * or some fetcher transition.\n * @returns \"idle\" | \"pending\"\n */\nexport function useGlobalPendingState() {\n  const isSubmitting = useGlobalSubmittingState() === 'submitting';\n  const isLoading = useGlobalLoadingState() === 'loading';\n\n  if (isLoading || isSubmitting) return 'pending';\n  return 'idle';\n}\n\n/**\n * const you know if the app is submitting some request, either global transition\n * or some fetcher transition.\n * @returns \"idle\" | \"submitting\"\n */\nexport function useGlobalSubmittingState() {\n  const states = useGlobalNavigationState();\n  if (states.includes('submitting')) return 'submitting';\n  return 'idle';\n}\n\n/**\n * const you know if the app is loading some request, either global transition\n * or some fetcher transition.\n * @returns \"idle\" | \"loading\"\n */\nexport function useGlobalLoadingState() {\n  const states = useGlobalNavigationState();\n  if (states.includes('loading')) return 'loading';\n  return 'idle';\n}\n"
  },
  {
    "path": "app/utils/react/hooks/useHeader.ts",
    "content": "import { useEffect, useMemo } from 'react';\nimport { useLocation, useMatches, useParams, type UIMatch } from '@remix-run/react';\nimport { useTranslation } from 'react-i18next';\n\nimport type { Handle } from '~/types/handle';\nimport { useHeaderStyle } from '~/store/layout/useHeaderStyle';\nimport { useLayout } from '~/store/layout/useLayout';\n\nfunction useHeaderOptions() {\n  const matches = useMatches() as UIMatch<unknown, Handle>[];\n  const location = useLocation();\n  const { t } = useTranslation();\n  const params = useParams();\n  const { backgroundColor } = useHeaderStyle((state) => state);\n\n  const isShowMobileHeader = useMemo(\n    () => !matches.some((match) => match.handle?.hideMobileHeader === true),\n    [matches],\n  );\n\n  const isShowTabLink = useMemo(\n    () => matches.some((match) => match.handle?.showTabLink === true),\n    [matches],\n  );\n  const isHideSidebar = useMemo(\n    () => matches.some((match) => match.handle?.hideSidebar === true),\n    [matches],\n  );\n\n  const hideTabLinkWithLocation = useMemo(() => {\n    const currentMatch = matches.find((match) => match.handle?.showTabLink);\n    if (currentMatch?.handle?.hideTabLinkWithLocation)\n      return currentMatch?.handle?.hideTabLinkWithLocation(location.pathname);\n    return false;\n  }, [matches, location.pathname]);\n\n  const customHeaderBackgroundColor = useMemo(\n    () => matches.some((match) => match.handle?.customHeaderBackgroundColor === true),\n    [matches],\n  );\n\n  const customHeaderChangeColorOnScroll = useMemo(\n    () => matches.some((match) => match.handle?.customHeaderChangeColorOnScroll === true),\n    [matches],\n  );\n\n  const currentMiniTitle = useMemo(() => {\n    const currentMatch = matches.filter((match) => match.handle?.miniTitle);\n    if (currentMatch?.length > 0 && currentMatch?.length < 2) {\n      return currentMatch[currentMatch.length - 1].handle?.miniTitle?.({\n        match: currentMatch[currentMatch.length - 1],\n        t, // for translations\n        params,\n      });\n    }\n    if (currentMatch?.length > 1) {\n      return currentMatch[currentMatch.length - 1].handle?.miniTitle?.({\n        match: currentMatch[currentMatch.length - 1],\n        parentMatch: currentMatch[currentMatch.length - 2], // for titles that need from parent route\n        t, // for translations\n        params,\n      });\n    }\n    return undefined;\n  }, [matches, params, t]);\n\n  const headerBackgroundColor = useMemo(() => {\n    if (customHeaderBackgroundColor) {\n      return backgroundColor;\n    }\n    return 'hsl(var(--theme-content1) / 0.6)';\n  }, [customHeaderBackgroundColor, backgroundColor]);\n\n  const isShowListViewChangeButton = useMemo(\n    () => matches.some((match) => match.handle?.showListViewChangeButton === true),\n    [matches],\n  );\n\n  return {\n    currentMiniTitle,\n    customHeaderBackgroundColor,\n    customHeaderChangeColorOnScroll,\n    headerBackgroundColor,\n    hideTabLinkWithLocation,\n    isShowListViewChangeButton,\n    isShowMobileHeader,\n    isShowTabLink,\n    isHideSidebar,\n  };\n}\n\nfunction useCustomHeaderChangePosition(intersection?: IntersectionObserverEntry) {\n  const { viewportRef } = useLayout((state) => state);\n  const { setStartChangeScrollPosition } = useHeaderStyle((state) => state);\n  useEffect(() => {\n    if (\n      viewportRef?.current &&\n      intersection?.intersectionRatio !== undefined &&\n      intersection?.intersectionRatio < 1\n    ) {\n      if (intersection?.boundingClientRect?.top < 150) {\n        setStartChangeScrollPosition(viewportRef?.current?.scrollTop - 64);\n      }\n    }\n  }, [intersection, viewportRef, setStartChangeScrollPosition]);\n}\n\nexport { useCustomHeaderChangePosition, useHeaderOptions };\n"
  },
  {
    "path": "app/utils/react/hooks/useHydrated.ts",
    "content": "import { useEffect, useState } from 'react';\n\nlet hydrating = true;\n\n/**\n * Return a boolean indicating if the JS has been hydrated already.\n * When doing Server-Side Rendering, the result will always be false.\n * When doing Client-Side Rendering, the result will always be false on the\n * first render and true from then on. Even if a new component renders it will\n * always start with true.\n *\n * Example: Disable a button that needs JS to work.\n * ```tsx\n * let hydrated = useHydrated();\n * return (\n *   <button type=\"button\" disabled={!hydrated} onClick={doSomethingCustom}>\n *     Click me\n *   </button>\n * );\n * ```\n */\nexport function useHydrated() {\n  const [hydrated, setHydrated] = useState(() => !hydrating);\n\n  useEffect(function hydrate() {\n    hydrating = false;\n    setHydrated(true);\n  }, []);\n\n  return hydrated;\n}\n"
  },
  {
    "path": "app/utils/react/hooks/useIsomorphicLayoutEffect.ts",
    "content": "import { useEffect, useLayoutEffect } from 'react';\n\nconst useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;\n\nexport default useIsomorphicLayoutEffect;\n"
  },
  {
    "path": "app/utils/react/hooks/useLocalStorage.ts",
    "content": "import { useLocalStorageValue } from '@react-hookz/web';\n\nfunction useSoraSettings() {\n  const currentSubtitleFontColor = useLocalStorageValue('sora_settings-subtitle-font_color', {\n    defaultValue: 'White',\n  });\n  const currentSubtitleFontSize = useLocalStorageValue('sora_settings-subtitle-font_size', {\n    defaultValue: '100%',\n  });\n  const currentSubtitleBackgroundColor = useLocalStorageValue(\n    'sora_settings-subtitle-background_color',\n    {\n      defaultValue: 'Black',\n    },\n  );\n  const currentSubtitleBackgroundOpacity = useLocalStorageValue(\n    'sora_settings-subtitle-background_opacity',\n    {\n      defaultValue: '0%',\n    },\n  );\n  const currentSubtitleWindowColor = useLocalStorageValue('sora_settings-subtitle-window_color', {\n    defaultValue: 'Black',\n  });\n  const currentSubtitleWindowOpacity = useLocalStorageValue(\n    'sora_settings-subtitle-window_opacity',\n    {\n      defaultValue: '0%',\n    },\n  );\n  const currentSubtitleTextEffects = useLocalStorageValue('sora_settings-subtitle-text_effect', {\n    defaultValue: 'Outline',\n  });\n  const autoShowSubtitle = useLocalStorageValue('sora_settings-subtitle-auto_show', {\n    defaultValue: false,\n  });\n  // const showFilter = useLocalStorageValue('sora_settings-layout-show-filter', {\n  //   defaultValue: false,\n  //   initializeWithValue: false,\n  // });\n  const isMutedTrailer = useLocalStorageValue('sora_settings-list-mute_trailer', {\n    defaultValue: true,\n    initializeWithValue: false,\n  });\n  const isPlayTrailer = useLocalStorageValue('sora_settings-list-play_trailer', {\n    defaultValue: false,\n    initializeWithValue: false,\n  });\n  const isFetchLogo = useLocalStorageValue('sora_settings-list-fetch_logo', {\n    defaultValue: false,\n  });\n  const isShowSpotlight = useLocalStorageValue('sora_settings-list-show_spotlight', {\n    defaultValue: false,\n  });\n  const isAutoSize = useLocalStorageValue('sora_settings-player-auto_size', {\n    defaultValue: false,\n  });\n  const isPicInPic = useLocalStorageValue('sora_settings-player-pic_in_pic', {\n    defaultValue: true,\n  });\n  const isMuted = useLocalStorageValue('sora_settings-player-mute', {\n    defaultValue: false,\n  });\n  const isAutoPlay = useLocalStorageValue('sora_settings-player-auto_play', {\n    defaultValue: false,\n  });\n  const isAutoMini = useLocalStorageValue('sora_settings-player-auto_mini', {\n    defaultValue: false,\n  });\n  const isLoop = useLocalStorageValue('sora_settings-player-loop', {\n    defaultValue: false,\n  });\n  const isScreenshot = useLocalStorageValue('sora_settings-player-screenshot', {\n    defaultValue: true,\n  });\n  const isMiniProgressBar = useLocalStorageValue('sora_settings-player-mini_progress_bar', {\n    defaultValue: true,\n  });\n  const isAutoPlayback = useLocalStorageValue('sora_settings-player-auto_playback', {\n    defaultValue: true,\n  });\n  const isAutoPlayNextEpisode = useLocalStorageValue(\n    'sora_settings-player-auto_play_next_episode',\n    {\n      defaultValue: true,\n    },\n  );\n  const isShowSkipOpEdButton = useLocalStorageValue('sora_settings-player-show_skip_op_ed_button', {\n    defaultValue: true,\n  });\n  const isAutoSkipOpEd = useLocalStorageValue('sora_settings-player-auto_skip_op_ed', {\n    defaultValue: false,\n  });\n  const isFastForward = useLocalStorageValue('sora_settings-player-fast_forward', {\n    defaultValue: true,\n  });\n  // const isSwipeFullscreen = useLocalStorageValue('sora_settings_player-gestures_swipe-fullscreen', {\n  //   defaultValue: false,\n  // });\n  const sidebarStyleMode = useLocalStorageValue('sora_settings-layout-sidebar-style_mode', {\n    defaultValue: 'rounded-all',\n  });\n  const sidebarMiniMode = useLocalStorageValue('sora_settings-layout-sidebar-mini_mode', {\n    defaultValue: false,\n    initializeWithValue: false,\n  });\n  const sidebarHoverMode = useLocalStorageValue('sora_settings-layout-sidebar-hover_mode', {\n    defaultValue: false,\n    initializeWithValue: false,\n  });\n  const sidebarBoxedMode = useLocalStorageValue('sora_settings-layout-sidebar-boxed_mode', {\n    defaultValue: false,\n    initializeWithValue: false,\n  });\n  const sidebarSheetMode = useLocalStorageValue('sora_settings-layout-sidebar-sheet_mode', {\n    defaultValue: false,\n    initializeWithValue: false,\n  });\n  const listViewType = useLocalStorageValue<'table' | 'card' | 'detail'>(\n    'sora_settings-layout-list_view',\n    {\n      defaultValue: 'card',\n      initializeWithValue: false,\n    },\n  );\n  const listLoadingType = useLocalStorageValue('sora_settings-layout-list-loading_type', {\n    defaultValue: 'pagination',\n    initializeWithValue: false,\n  });\n  const autoSwitchSubtitle = useLocalStorageValue('sora_settings-subtitle-auto_switch', {\n    defaultValue: true,\n  });\n  const isShowBreadcrumb = useLocalStorageValue('sora_settings-layout-header-show_breadcrumb', {\n    defaultValue: true,\n    initializeWithValue: false,\n  });\n  const isShowTopPagination = useLocalStorageValue(\n    'sora_settings-layout-list-show-top-pagination',\n    {\n      defaultValue: false,\n      initializeWithValue: false,\n    },\n  );\n  const isLightDarkThemeOnly = useLocalStorageValue('sora_settings-layout-theme-light_dark_only', {\n    defaultValue: true,\n  });\n  const currentThemeColor = useLocalStorageValue('sora_settings-layout-theme-color', {\n    defaultValue: 'blue',\n    initializeWithValue: false,\n  });\n\n  return {\n    currentSubtitleFontColor,\n    currentSubtitleFontSize,\n    currentSubtitleBackgroundColor,\n    currentSubtitleBackgroundOpacity,\n    currentSubtitleWindowColor,\n    currentSubtitleWindowOpacity,\n    autoShowSubtitle,\n    // showFilter,\n    isMutedTrailer,\n    isPlayTrailer,\n    isAutoSize,\n    isPicInPic,\n    isMuted,\n    isAutoPlay,\n    isAutoMini,\n    isLoop,\n    isScreenshot,\n    isMiniProgressBar,\n    isAutoPlayback,\n    isAutoPlayNextEpisode,\n    isShowSkipOpEdButton,\n    isAutoSkipOpEd,\n    isFastForward,\n    // isSwipeFullscreen,\n    currentSubtitleTextEffects,\n    sidebarStyleMode,\n    sidebarMiniMode,\n    sidebarHoverMode,\n    sidebarBoxedMode,\n    sidebarSheetMode,\n    listViewType,\n    listLoadingType,\n    autoSwitchSubtitle,\n    isShowBreadcrumb,\n    isShowTopPagination,\n    isLightDarkThemeOnly,\n    currentThemeColor,\n    isFetchLogo,\n    isShowSpotlight,\n  };\n}\n\nexport { useSoraSettings };\n"
  },
  {
    "path": "app/utils/react/hooks/useSplitArrayIntoPage.ts",
    "content": "import { useMemo, useState } from 'react';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst useSplitArrayIntoPage = (array: any[], pageSize: number) => {\n  const [currentPage, setCurrentPage] = useState(1);\n  const maxPage = Math.ceil(array.length / pageSize);\n\n  const currentData = useMemo(() => {\n    const begin = (currentPage - 1) * pageSize;\n    const end = begin + pageSize;\n    return array.slice(begin, end);\n  }, [currentPage, pageSize, array]);\n\n  const gotoPage = (page: number) => {\n    const pageNumber = Math.max(1, page);\n    setCurrentPage(Math.min(pageNumber, maxPage));\n  };\n\n  const nextPage = () => {\n    gotoPage(currentPage + 1);\n  };\n\n  const previousPage = () => {\n    gotoPage(currentPage - 1);\n  };\n\n  const canNextPage = currentPage < maxPage;\n  const canPreviousPage = currentPage > 1;\n\n  return {\n    next: nextPage,\n    previous: previousPage,\n    canNext: canNextPage,\n    canPrevious: canPreviousPage,\n    gotoPage,\n    currentPage,\n    maxPage,\n    currentData,\n  };\n};\n\nexport default useSplitArrayIntoPage;\n"
  },
  {
    "path": "app/utils/react/hooks/useToast.ts",
    "content": "import { useEffect } from 'react';\nimport { toast } from 'sonner';\n\nimport type { ToastMessage } from '~/utils/server/toast-session.server';\n\nexport const useToast = (message?: ToastMessage) => {\n  useEffect(() => {\n    if (message) {\n      switch (message.type) {\n        case 'default':\n          toast(message.title, {\n            description: message.description,\n          });\n          break;\n        case 'success':\n          toast.success(message.title, {\n            description: message.description,\n          });\n          break;\n        case 'error':\n          toast.error(message.title, {\n            description: message.description,\n          });\n          break;\n        default:\n          throw new Error(`${message.type} is not handled`);\n      }\n    }\n  }, [message]);\n};\n"
  },
  {
    "path": "app/utils/react/hooks/useTypedRouteLoaderData.ts",
    "content": "import type { SerializeFrom } from '@remix-run/node';\nimport { useRouteLoaderData } from '@remix-run/react';\nimport type { loader as RootLoader } from '~/root';\n\nimport type { loader as RoutesIndexLoader } from '~/routes/_index';\nimport type { loader as RoutesAnimesIndexLoader } from '~/routes/anime+/_index';\nimport type { loader as RoutesAnimeLoader } from '~/routes/anime+/$animeId';\nimport type { loader as RoutesAnimeEpisodeWatchLoader } from '~/routes/anime+/$animeId_.episode.$episodeId.watch';\nimport type { loader as RoutesAnimeEpisodesLoader } from '~/routes/anime+/$animeId+/episodes';\nimport type { loader as RoutesPopularAnimeLoader } from '~/routes/anime+/popular';\nimport type { loader as RoutesRecentAnimeLoader } from '~/routes/anime+/recent-episodes';\nimport type { loader as RoutesTrendingAnimeLoader } from '~/routes/anime+/trending';\nimport type { loader as RoutesDiscoverLoader } from '~/routes/discover+/anime';\nimport type { loader as RoutesDiscoverMoviesLoader } from '~/routes/discover+/movies';\nimport type { loader as RoutesDiscoverTvsLoader } from '~/routes/discover+/tv-shows';\nimport type { loader as RoutesListLoader } from '~/routes/lists+/$listId';\nimport type { loader as RoutesMoviesIndexLoader } from '~/routes/movies+/_index';\nimport type { loader as RoutesMovieLoader } from '~/routes/movies+/$movieId';\nimport type { loader as RoutesMovieWatchLoader } from '~/routes/movies+/$movieId_.watch';\nimport type { loader as RoutesMovieIndexLoader } from '~/routes/movies+/$movieId+/_index';\nimport type { loader as RoutesMovieCastsLoader } from '~/routes/movies+/$movieId+/cast';\nimport type { loader as RoutesMovieCrewsLoader } from '~/routes/movies+/$movieId+/crew';\nimport type { loader as RoutesMoviePhotosLoader } from '~/routes/movies+/$movieId+/photos';\nimport type { loader as RoutesMovieRecommendationsLoader } from '~/routes/movies+/$movieId+/recommendations';\nimport type { loader as RoutesMovieSimilarLoader } from '~/routes/movies+/$movieId+/similar';\nimport type { loader as RoutesMovieVideosLoader } from '~/routes/movies+/$movieId+/videos';\nimport type { loader as RoutesPopularMoviesLoader } from '~/routes/movies+/popular';\nimport type { loader as RoutesTopRatedMoviesLoader } from '~/routes/movies+/top-rated';\nimport type { loader as RoutesUpcomingMoviesLoader } from '~/routes/movies+/upcoming';\nimport type { loader as RoutesPeopleIndexLoader } from '~/routes/people+/_index';\nimport type { loader as RoutesPersonLoader } from '~/routes/people+/$peopleId';\nimport type { loader as RoutesPersonCreditsLoader } from '~/routes/people+/$peopleId+/credits';\nimport type { loader as RoutesPersonMediaLoader } from '~/routes/people+/$peopleId+/media';\nimport type { loader as RoutesSearchAnimeIndexLoader } from '~/routes/search+/anime+/_index';\nimport type { loader as RoutesSearchAnimeLoader } from '~/routes/search+/anime+/$animeKeyword';\nimport type { loader as RoutesSearchMovieIndexLoader } from '~/routes/search+/movie+/_index';\nimport type { loader as RoutesSearchMovieLoader } from '~/routes/search+/movie+/$movieKeyword';\nimport type { loader as RoutesSearchPeopleIndexLoader } from '~/routes/search+/people+/_index';\nimport type { loader as RoutesSearchPeopleLoader } from '~/routes/search+/people+/$peopleKeyword';\nimport type { loader as RoutesSearchTvIndexLoader } from '~/routes/search+/tv+/_index';\nimport type { loader as RoutesSearchTvLoader } from '~/routes/search+/tv+/$tvKeyword';\nimport type { loader as RoutesTvsIndexLoader } from '~/routes/tv-shows+/_index';\nimport type { loader as RoutesTvLoader } from '~/routes/tv-shows+/$tvId';\nimport type { loader as RoutesTvSeasonLoader } from '~/routes/tv-shows+/$tvId_.season.$seasonId';\nimport type { loader as RoutesTvSeasonEpisodeWatchLoader } from '~/routes/tv-shows+/$tvId_.season.$seasonId_.episode.$episodeId.watch';\nimport type { loader as RoutesTvSeasonCastsLoader } from '~/routes/tv-shows+/$tvId_.season.$seasonId+/cast';\nimport type { loader as RoutesTvSeasonCrewsLoader } from '~/routes/tv-shows+/$tvId_.season.$seasonId+/crew';\nimport type { loader as RoutesTvSeasonPhotosLoader } from '~/routes/tv-shows+/$tvId_.season.$seasonId+/photos';\nimport type { loader as RoutesTvSeasonVideosLoader } from '~/routes/tv-shows+/$tvId_.season.$seasonId+/videos';\nimport type { loader as RoutesTvIndexLoader } from '~/routes/tv-shows+/$tvId+/_index';\nimport type { loader as RoutesTvCastsLoader } from '~/routes/tv-shows+/$tvId+/cast';\nimport type { loader as RoutesTvCrewsLoader } from '~/routes/tv-shows+/$tvId+/crew';\nimport type { loader as RoutesTvPhotosLoader } from '~/routes/tv-shows+/$tvId+/photos';\nimport type { loader as RoutesTvRecommendationsLoader } from '~/routes/tv-shows+/$tvId+/recommendations';\nimport type { loader as RoutesTvSimilarLoader } from '~/routes/tv-shows+/$tvId+/similar';\nimport type { loader as RoutesTvVideosLoader } from '~/routes/tv-shows+/$tvId+/videos';\nimport type { loader as RoutesOnTvLoader } from '~/routes/tv-shows+/on-the-air';\nimport type { loader as RoutesPopularTvsLoader } from '~/routes/tv-shows+/popular';\nimport type { loader as RoutesTopRatedTvsLoader } from '~/routes/tv-shows+/top-rated';\nimport type { loader as RoutesWatchHistoryLoader } from '~/routes/watch-history';\n\ntype Loaders = {\n  root: typeof RootLoader;\n  'routes/_index': typeof RoutesIndexLoader;\n  'routes/watch-history': typeof RoutesWatchHistoryLoader;\n  'routes/anime+/$animeId_.episode.$episodeId.watch': typeof RoutesAnimeEpisodeWatchLoader;\n  'routes/anime+/$animeId': typeof RoutesAnimeLoader;\n  'routes/anime+/_index': typeof RoutesAnimesIndexLoader;\n  'routes/anime+/popular': typeof RoutesPopularAnimeLoader;\n  'routes/anime+/recent-episodes': typeof RoutesRecentAnimeLoader;\n  'routes/anime+/trending': typeof RoutesTrendingAnimeLoader;\n  'routes/anime+/$animeId+/episodes': typeof RoutesAnimeEpisodesLoader;\n  'routes/lists+/$listId': typeof RoutesListLoader;\n  'routes/discover+/anime': typeof RoutesDiscoverLoader;\n  'routes/discover+/movies': typeof RoutesDiscoverMoviesLoader;\n  'routes/discover+/tv-shows': typeof RoutesDiscoverTvsLoader;\n  'routes/movies+/$movieId': typeof RoutesMovieLoader;\n  'routes/movies+/$movieId_.watch': typeof RoutesMovieWatchLoader;\n  'routes/movies+/_index': typeof RoutesMoviesIndexLoader;\n  'routes/movies+/popular': typeof RoutesPopularMoviesLoader;\n  'routes/movies+/top-rated': typeof RoutesTopRatedMoviesLoader;\n  'routes/movies+/upcoming': typeof RoutesUpcomingMoviesLoader;\n  'routes/movies+/$movieId+/cast': typeof RoutesMovieCastsLoader;\n  'routes/movies+/$movieId+/crew': typeof RoutesMovieCrewsLoader;\n  'routes/movies+/$movieId+/recommendations': typeof RoutesMovieRecommendationsLoader;\n  'routes/movies+/$movieId+/_index': typeof RoutesMovieIndexLoader;\n  'routes/movies+/$movieId+/photos': typeof RoutesMoviePhotosLoader;\n  'routes/movies+/$movieId+/videos': typeof RoutesMovieVideosLoader;\n  'routes/movies+/$movieId+/similar': typeof RoutesMovieSimilarLoader;\n  'routes/people+/$peopleId': typeof RoutesPersonLoader;\n  'routes/people+/_index': typeof RoutesPeopleIndexLoader;\n  'routes/people+/$peopleId.credits': typeof RoutesPersonCreditsLoader;\n  'routes/people+/$peopleId.media': typeof RoutesPersonMediaLoader;\n  'routes/search+/anime+/$animeKeyword': typeof RoutesSearchAnimeLoader;\n  'routes/search+/anime+/_index': typeof RoutesSearchAnimeIndexLoader;\n  'routes/search+/movie+/$movieKeyword': typeof RoutesSearchMovieLoader;\n  'routes/search+/movie+/_index': typeof RoutesSearchMovieIndexLoader;\n  'routes/search+/people+/$peopleKeyword': typeof RoutesSearchPeopleLoader;\n  'routes/search+/people+/_index': typeof RoutesSearchPeopleIndexLoader;\n  'routes/search+/tv+/$tvKeyword': typeof RoutesSearchTvLoader;\n  'routes/search+/tv+/_index': typeof RoutesSearchTvIndexLoader;\n  'routes/tv-shows+/$tvId_.season.$seasonId.episode.$episodeId.watch': typeof RoutesTvSeasonEpisodeWatchLoader;\n  'routes/tv-shows+/$tvId_.season.$seasonId': typeof RoutesTvSeasonLoader;\n  'routes/tv-shows+/$tvId': typeof RoutesTvLoader;\n  'routes/tv-shows+/_index': typeof RoutesTvsIndexLoader;\n  'routes/tv-shows+/popular': typeof RoutesPopularTvsLoader;\n  'routes/tv-shows+/on-the-air': typeof RoutesOnTvLoader;\n  'routes/tv-shows+/top-rated': typeof RoutesTopRatedTvsLoader;\n  'routes/tv-shows+/$tvId+/cast': typeof RoutesTvCastsLoader;\n  'routes/tv-shows+/$tvId+/crew': typeof RoutesTvCrewsLoader;\n  'routes/tv-shows+/$tvId+/recommendations': typeof RoutesTvRecommendationsLoader;\n  'routes/tv-shows+/$tvId+/_index': typeof RoutesTvIndexLoader;\n  'routes/tv-shows+/$tvId+/photos': typeof RoutesTvPhotosLoader;\n  'routes/tv-shows+/$tvId+/videos': typeof RoutesTvVideosLoader;\n  'routes/tv-shows+/$tvId+/similar': typeof RoutesTvSimilarLoader;\n  'routes/tv-shows+/$tvId.season.$seasonId+/cast': typeof RoutesTvSeasonCastsLoader;\n  'routes/tv-shows+/$tvId.season.$seasonId+/crew': typeof RoutesTvSeasonCrewsLoader;\n  'routes/tv-shows+/$tvId.season.$seasonId+/photos': typeof RoutesTvSeasonPhotosLoader;\n  'routes/tv-shows+/$tvId.season.$seasonId+/videos': typeof RoutesTvSeasonVideosLoader;\n};\n\nexport function useTypedRouteLoaderData<T extends keyof Loaders>(route: T) {\n  return useRouteLoaderData(route) as SerializeFrom<Loaders[T]>;\n}\n"
  },
  {
    "path": "app/utils/server/cache.server.ts",
    "content": "import { lruCacheAdapter, verboseReporter, type CacheEntry } from 'cachified';\nimport * as C from 'cachified';\nimport { LRUCache } from 'lru-cache';\n\nimport { singleton } from './singleton.server';\n\nconst lruOptions = {\n  // let just say 1kb is an object which after being stringified having length of 1000\n  // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n  sizeCalculation: (_value: unknown, _key: string) =>\n    Math.ceil(JSON.stringify(_value).length / 1000),\n\n  // we won't cache object bigger than 800kb\n  maxSize: 800,\n\n  // max nb of objects, less than 500mb\n  max: 640,\n};\n\nconst lru = singleton('lru-cache', () => new LRUCache<string, CacheEntry<unknown>>(lruOptions));\n\nconst lruCache = lruCacheAdapter(lru);\n\nconst getAllCacheKeys = async () => [...lru.keys()];\nconst searchCacheKeys = async (search: string) =>\n  [...lru.keys()].filter((key) => key.includes(search));\n\nasync function shouldForceFresh({\n  forceFresh,\n  request,\n  key,\n}: {\n  forceFresh?: boolean | string;\n  request?: Request;\n  key: string;\n}) {\n  if (typeof forceFresh === 'boolean') return forceFresh;\n  if (typeof forceFresh === 'string') return forceFresh.split(',').includes(key);\n  if (!request) return false;\n  const fresh = new URL(request.url).searchParams.get('fresh');\n  if (typeof fresh !== 'string') return false;\n  if (fresh === '') return true;\n  return fresh.split(',').includes(key);\n}\n\nasync function cachified<Value>({\n  request,\n  ...options\n}: Omit<C.CachifiedOptions<Value>, 'forceFresh'> & {\n  request?: Request;\n  forceFresh?: boolean | string;\n}): Promise<Value> {\n  let cachifiedResolved = false;\n  const cachifiedPromise = C.cachified({\n    reporter: verboseReporter(),\n    ...options,\n    forceFresh: await shouldForceFresh({\n      forceFresh: options.forceFresh,\n      request,\n      key: options.key,\n    }),\n    getFreshValue: async (context) => {\n      if (!cachifiedResolved) {\n        const freshValue = await options.getFreshValue(context);\n        return freshValue;\n      }\n      return options.getFreshValue(context);\n    },\n  });\n  cachifiedResolved = true;\n  return cachifiedPromise;\n}\n\nasync function fetcher<Value>({\n  url,\n  ...options\n}: Omit<C.CachifiedOptions<Value>, 'getFreshValue' | 'forceFresh'> & {\n  url: string;\n  forceFresh?: boolean | string;\n  getFreshValue?: undefined;\n}): Promise<Value> {\n  const results = await cachified({\n    ...options,\n    request: undefined,\n    getFreshValue: async () => {\n      const res = await fetch(url);\n      if (!res.ok) throw new Error(JSON.stringify(await res.json()));\n      const data = (await res.json()) as Value;\n      return data;\n    },\n  });\n  return results;\n}\n\nexport { lru, lruCache, getAllCacheKeys, searchCacheKeys, shouldForceFresh, cachified, fetcher };\n"
  },
  {
    "path": "app/utils/server/http.ts",
    "content": "/* eslint-disable import/prefer-default-export */\nexport const CACHE_CONTROL = {\n  home: 'private, max-age=300',\n  default: 'private, max-age=300',\n  popular: `public, max-age=${60 * 60 * 5}, s-maxage=1, stale-while-revalidate=${60 * 60 * 24}`,\n  nowPlaying: `public, max-age=${60 * 60 * 5}, s-maxage=1, stale-while-revalidate=${60 * 60 * 24}`,\n  trending: `public, max-age=${60 * 60}, s-maxage=1, stale-while-revalidate=${60 * 60 * 24}`,\n  airingToday: `public, max-age=${60 * 60}, s-maxage=1, stale-while-revalidate=${60 * 60 * 24}`,\n  topRated: `public, max-age=${60 * 60 * 5}, s-maxage=1, stale-while-revalidate=${60 * 60 * 24}`,\n  discover: `private, max-age=${60 * 5}`,\n  search: `public, max-age=${60 * 60}, s-maxage=1, stale-while-revalidate=${60 * 60 * 24}`,\n  watch: `private, max-age=${60 * 5}`,\n  movie: `public, max-age=${60 * 60}, s-maxage=1, stale-while-revalidate=${60 * 60 * 24}`,\n  tv: `public, max-age=${60 * 60}, s-maxage=1, stale-while-revalidate=${60 * 60 * 24}`,\n  anime: `public, max-age=${60 * 60}, s-maxage=1, stale-while-revalidate=${60 * 60 * 24}`,\n  person: `public, max-age=${60 * 60}, s-maxage=1, stale-while-revalidate=${60 * 60 * 24}`,\n  collection: `public, max-age=${60 * 60 * 5}, s-maxage=1, stale-while-revalidate=${60 * 60 * 24}`,\n  detail: `public, max-age=${60 * 60 * 5}, s-maxage=1, stale-while-revalidate=${60 * 60 * 24}`,\n  season: `private, max-age=${60 * 5}`,\n  episode: `private, max-age=${60 * 5}`,\n};\n"
  },
  {
    "path": "app/utils/server/og.server.tsx",
    "content": "/* eslint-disable @typescript-eslint/ban-ts-comment */\n/* eslint-disable @typescript-eslint/indent */\n\nimport { renderAsync } from '@resvg/resvg-js';\n\nconst generateSvg = async ({ title, cover }: { title: string; cover: string }) => {\n  const res = await fetch(\n    'https://github.com/rsms/inter/raw/master/docs/font-files/Inter-Regular.woff',\n  );\n  const font = await res.arrayBuffer();\n  const { default: satori } = await import('satori');\n  return satori(\n    <div\n      style={{\n        display: 'flex',\n        height: '100%',\n        width: '100%',\n        alignItems: 'center',\n        justifyContent: 'center',\n        flexDirection: 'row',\n        fontSize: 96,\n        letterSpacing: -2,\n        fontWeight: 700,\n        textAlign: 'center',\n        gap: 36,\n      }}\n    >\n      <div\n        style={{\n          width: '1200px',\n          height: '600px',\n          backgroundImage: `url(${cover})`,\n          backgroundSize: 'cover',\n          backgroundRepeat: 'no-repeat',\n          position: 'absolute',\n          zIndex: '0',\n          filter: 'blur(14px) saturate(120%) brightness(120%)',\n        }}\n      />\n      <div\n        style={{\n          width: '100%',\n          height: '100%',\n          position: 'absolute',\n          zIndex: '0',\n          backgroundColor: 'rgba(0,0,0,0.7)',\n        }}\n      />\n      <img\n        src=\"https://raw.githubusercontent.com/Khanhtran47/Sora/master/app/assets/images/logo_loading.png\"\n        height={150}\n        width={150}\n        alt=\"logo\"\n        style={{\n          borderRadius: '50%',\n        }}\n      />\n      <div\n        style={{\n          backgroundImage:\n            'linear-gradient(112deg, rgb(6,183,219) -63.59%, rgb(255,78,205) -20.3%, rgb(0,114,245) 70.46%)',\n          backgroundClip: 'text',\n          // @ts-ignore\n          '-webkit-background-clip': 'text',\n          color: 'transparent',\n          fontFamily: 'Inter',\n        }}\n      >\n        {title}\n      </div>\n    </div>,\n    {\n      width: 1200,\n      height: 600,\n      fonts: [\n        {\n          name: 'Inter',\n          data: font,\n          weight: 400,\n          style: 'normal',\n        },\n      ],\n    },\n  );\n};\n\nconst generateMovieSvg = async ({\n  title = '',\n  cover = '',\n  poster = '',\n  voteAverage = 0,\n  genres = [],\n  releaseYear,\n  numberOfEpisodes,\n  numberOfSeasons,\n  runtime,\n  productionCompany,\n}: {\n  title?: string;\n  cover?: string;\n  poster?: string;\n  voteAverage?: number | string;\n  genres?:\n    | {\n        id?: number | undefined;\n        name?: string | undefined;\n      }[]\n    | undefined;\n  releaseYear?: number;\n  numberOfEpisodes?: number;\n  numberOfSeasons?: number;\n  runtime?: number | null;\n  productionCompany?: string;\n}) => {\n  const res = await fetch(\n    'https://github.com/rsms/inter/raw/master/docs/font-files/Inter-Regular.woff',\n  );\n  const font = await res.arrayBuffer();\n\n  const { default: satori } = await import('satori');\n  const color = '#0072F5';\n  return satori(\n    <div\n      style={{\n        width: '100%',\n        height: '100%',\n        position: 'relative',\n        display: 'flex',\n        fontFamily: 'Inter',\n      }}\n    >\n      <div\n        style={{\n          width: '1200px',\n          height: '620px',\n          backgroundImage: `url(${cover})`,\n          backgroundSize: 'cover',\n          backgroundRepeat: 'no-repeat',\n          position: 'absolute',\n          top: -10,\n          left: -200,\n          zIndex: '0',\n          filter: 'blur(14px) saturate(120%) brightness(120%)',\n        }}\n      />\n      <div\n        style={{\n          width: '100%',\n          height: '100%',\n          position: 'absolute',\n          zIndex: '0',\n          backgroundColor: 'rgba(0,0,0,0.7)',\n        }}\n      />\n      <div\n        style={{\n          width: 800,\n          height: '100%',\n          position: 'absolute',\n          zIndex: '1',\n          top: 0,\n          left: 0,\n          padding: '30px 0 50px 50px',\n          display: 'flex',\n          flexDirection: 'column',\n        }}\n      >\n        <h3\n          style={{\n            backgroundImage:\n              'linear-gradient(112deg, rgb(6,183,219) -63.59%, rgb(255,78,205) -20.3%, rgb(0,114,245) 70.46%)',\n            backgroundClip: 'text',\n            // @ts-ignore\n            '-webkit-background-clip': 'text',\n            color: 'transparent',\n            fontFamily: 'Inter',\n            fontSize: 28,\n            marginBottom: 0,\n          }}\n        >\n          SORA\n        </h3>\n        <h4\n          style={{\n            color: 'rgba(236,237,238,0.6)',\n            fontSize: 20,\n            marginBottom: 0,\n            fontFamily: 'Inter',\n          }}\n        >\n          {releaseYear ? `${releaseYear}` : null}\n          <div style={{ marginRight: 10 }} />\n          {runtime ? `• ${runtime} min` : null}\n          {numberOfEpisodes && numberOfSeasons ? (\n            <>\n              {`• ${numberOfSeasons} seasons`}\n              <div style={{ marginRight: 10 }} />\n              {`• ${numberOfEpisodes} episodes`}\n            </>\n          ) : null}\n        </h4>\n        <h1\n          style={{\n            color: '#ECEDEE',\n            fontSize: 50,\n            width: '90%',\n            marginBottom: 0,\n            fontFamily: 'Inter',\n          }}\n        >\n          {title.length > 80 ? `${title.substring(0, 80)}...` : title}\n        </h1>\n        <h4\n          style={{\n            fontWeight: 500,\n            color,\n            fontSize: 24,\n            marginBottom: 0,\n            marginTop: 10,\n            fontFamily: 'Inter',\n          }}\n        >\n          {productionCompany}\n        </h4>\n        <div\n          style={{\n            display: 'flex',\n            fontSize: 36,\n            alignItems: 'center',\n            marginTop: 'auto',\n            gap: 5,\n            marginBottom: 15,\n            fontWeight: 600,\n            color: 'white',\n            fill: color,\n            fontFamily: 'Inter',\n          }}\n        >\n          <svg\n            width=\"36\"\n            height=\"36\"\n            viewBox=\"0 0 24 24\"\n            fill=\"none\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n          >\n            <path\n              d=\"M17.9185 14.3201C17.6595 14.5711 17.5405 14.9341 17.5995 15.2901L18.4885 20.2101C18.5635 20.6271 18.3875 21.0491 18.0385 21.2901C17.6965 21.5401 17.2415 21.5701 16.8685 21.3701L12.4395 19.0601C12.2855 18.9781 12.1145 18.9341 11.9395 18.9291H11.6685C11.5745 18.9431 11.4825 18.9731 11.3985 19.0191L6.96851 21.3401C6.74951 21.4501 6.50151 21.4891 6.25851 21.4501C5.66651 21.3381 5.27151 20.7741 5.36851 20.1791L6.25851 15.2591C6.31751 14.9001 6.19851 14.5351 5.93951 14.2801L2.32851 10.7801C2.02651 10.4871 1.92151 10.0471 2.05951 9.65011C2.19351 9.25411 2.53551 8.96511 2.94851 8.90011L7.91851 8.17911C8.29651 8.14011 8.62851 7.91011 8.79851 7.57011L10.9885 3.08011C11.0405 2.98011 11.1075 2.88811 11.1885 2.81011L11.2785 2.74011C11.3255 2.68811 11.3795 2.64511 11.4395 2.61011L11.5485 2.57011L11.7185 2.50011H12.1395C12.5155 2.53911 12.8465 2.76411 13.0195 3.10011L15.2385 7.57011C15.3985 7.89711 15.7095 8.12411 16.0685 8.17911L21.0385 8.90011C21.4585 8.96011 21.8095 9.25011 21.9485 9.65011C22.0795 10.0511 21.9665 10.4911 21.6585 10.7801L17.9185 14.3201Z\"\n              fill={color}\n            />\n          </svg>\n          <span>{voteAverage}</span>\n        </div>\n        <div\n          style={{\n            display: 'flex',\n            gap: 8,\n            width: '90%',\n            flexWrap: 'wrap',\n          }}\n        >\n          {genres?.map((genre) => (\n            <div\n              style={{\n                background: 'rgba(80, 80, 80, 0.4)',\n                padding: '4px 15px',\n                fontSize: 20,\n                borderRadius: 50,\n                color: 'white',\n                fontWeight: 200,\n                fontFamily: 'Inter',\n              }}\n              key={genre.id}\n            >\n              {genre.name?.replace(/[&\\\\/\\\\#,+()$~%.'\":*?<>{}]/g, ' ')}\n            </div>\n          ))}\n        </div>\n      </div>\n      <img\n        src={poster}\n        alt=\"poster\"\n        style={{\n          width: 414,\n          height: 620,\n          position: 'absolute',\n          objectFit: 'cover',\n          zIndex: '1',\n          top: 0,\n          right: 0,\n          boxShadow: '-10px 5px 20px 0px rgba(0,0,0,0.75)',\n        }}\n      />\n    </div>,\n    {\n      width: 1200,\n      height: 600,\n      fonts: [\n        {\n          name: 'Inter',\n          data: font,\n          weight: 400,\n          style: 'normal',\n        },\n      ],\n    },\n  );\n};\n\nconst generatePng = async (svg: string) => {\n  const image = await renderAsync(svg, {\n    fitTo: {\n      mode: 'width',\n      value: 1200,\n    },\n    font: {\n      loadSystemFonts: false,\n    },\n  });\n  return new Response(image.asPng(), {\n    headers: {\n      'content-type': 'image/png',\n      'cache-control':\n        process.env.NODE_ENV === 'development'\n          ? 'no-cache, no-store'\n          : 'public, immutable, no-transform, max-age=31536000',\n    },\n  });\n};\n\nexport { generateSvg, generateMovieSvg, generatePng };\n"
  },
  {
    "path": "app/utils/server/singleton.server.ts",
    "content": "// since the dev server re-requires the bundle, do some shenanigans to make\n// certain things persist across that 😆\n// Borrowed/modified from https://github.com/jenseng/abuse-the-platform/blob/2993a7e846c95ace693ce61626fa072174c8d9c7/app/utils/singleton.ts\n\nexport function singleton<Value>(name: string, value: () => Value): Value {\n  const yolo = global as any;\n  yolo.__singletons ??= {};\n  yolo.__singletons[name] ??= value();\n  return yolo.__singletons[name];\n}\n"
  },
  {
    "path": "app/utils/server/toast-session.server.ts",
    "content": "import { createCookieSessionStorage, redirect } from '@remix-run/node';\n\nconst TOAST_SESSION = 'toast';\n\nexport type ToastMessage = {\n  title: string;\n  type: 'default' | 'success' | 'error';\n  description?: string;\n};\n\nexport const sessionStorage = createCookieSessionStorage({\n  cookie: {\n    name: TOAST_SESSION,\n    sameSite: 'lax',\n    path: '/',\n    httpOnly: true,\n    secrets: [process.env.SESSION_KEY || 's3cret1'],\n    secure: process.env.NODE_ENV === 'production',\n  },\n});\n\nfunction getSessionFromRequest(request: Request) {\n  const cookie = request.headers.get('Cookie');\n  return sessionStorage.getSession(cookie);\n}\n\n/**\n * Helper method used to add flash session values to the session\n */\nexport async function flashMessageHeaders(\n  request: Request,\n  toastMessage: ToastMessage,\n  headers?: ResponseInit['headers'],\n) {\n  const cookie = await addToast(request, toastMessage);\n  const newHeaders = new Headers(headers);\n  newHeaders.append('Set-Cookie', cookie);\n  return newHeaders;\n}\n\nexport async function redirectWithToast(\n  request: Request,\n  url: string,\n  toastMessage: ToastMessage,\n  init?: ResponseInit,\n) {\n  return redirect(url, {\n    ...init,\n    headers: await flashMessageHeaders(request, toastMessage, init?.headers),\n  });\n}\n\nexport function addToast(request: Request, toastMessage: ToastMessage): Promise<string> {\n  const toasts = getToastSession(request);\n  toasts.add(toastMessage);\n  return toasts.commit();\n}\n\nexport function getToastSession(request: Request) {\n  let nextMessage: ToastMessage | undefined;\n\n  async function getMessage(): Promise<ToastMessage> {\n    const session = await getSessionFromRequest(request);\n    const toast = (JSON.parse(session.get(TOAST_SESSION) || 'null') as ToastMessage) || undefined;\n    return toast;\n  }\n\n  async function commit(): Promise<string> {\n    const session = await getSessionFromRequest(request);\n    session.flash(TOAST_SESSION, JSON.stringify(nextMessage));\n    return sessionStorage.commitSession(session);\n  }\n\n  function add(toastMessage: ToastMessage): void {\n    nextMessage = toastMessage;\n  }\n\n  return {\n    getMessage,\n    commit,\n    add,\n  };\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"sora\",\n  \"private\": true,\n  \"sideEffects\": false,\n  \"license\": \"GPL-3.0\",\n  \"scripts\": {\n    \"build\": \"run-s \\\"build:*\\\"\",\n    \"build:worker\": \"remix-pwa build\",\n    \"build:remix\": \"cross-env NODE_ENV=production remix build\",\n    \"clean\": \"rimraf .cache ./public/build ./public/sw.js ./build\",\n    \"dev\": \"run-p \\\"dev:*\\\"\",\n    \"dev:worker\": \"remix-pwa dev\",\n    \"dev:remix\": \"cross-env NODE_ENV=development remix dev --manual\",\n    \"format-code\": \"pnpm run prettier:fix & pnpm run lint:fix\",\n    \"lint\": \"eslint --cache --cache-location ./node_modules/.cache/eslint .\",\n    \"lint:fix\": \"pnpm run lint --fix\",\n    \"prebuild\": \"pnpm cleanup\",\n    \"prepare\": \"husky install\",\n    \"prettier\": \"prettier app --check\",\n    \"prettier:fix\": \"prettier app --write\",\n    \"start\": \"dotenv -e .env remix-serve ./build/index.js\",\n    \"typecheck\": \"tsc\"\n  },\n  \"dependencies\": {\n    \"@consumet/extensions\": \"github:consumet/consumet.ts\",\n    \"@fontsource/sora\": \"^5.0.14\",\n    \"@lottiefiles/react-lottie-player\": \"^3.5.3\",\n    \"@nasa-gcn/remix-seo\": \"^2.0.0\",\n    \"@next-boost/hybrid-disk-cache\": \"^0.3.0\",\n    \"@nextui-org/accordion\": \"^2.0.23\",\n    \"@nextui-org/avatar\": \"^2.0.21\",\n    \"@nextui-org/badge\": \"^2.0.19\",\n    \"@nextui-org/button\": \"^2.0.21\",\n    \"@nextui-org/card\": \"^2.0.21\",\n    \"@nextui-org/checkbox\": \"^2.0.22\",\n    \"@nextui-org/chip\": \"^2.0.21\",\n    \"@nextui-org/divider\": \"^2.0.20\",\n    \"@nextui-org/image\": \"^2.0.21\",\n    \"@nextui-org/input\": \"^2.1.9\",\n    \"@nextui-org/kbd\": \"^2.0.20\",\n    \"@nextui-org/link\": \"^2.0.22\",\n    \"@nextui-org/pagination\": \"^2.0.22\",\n    \"@nextui-org/progress\": \"^2.0.21\",\n    \"@nextui-org/shared-icons\": \"^2.0.4\",\n    \"@nextui-org/skeleton\": \"^2.0.19\",\n    \"@nextui-org/spacer\": \"^2.0.19\",\n    \"@nextui-org/spinner\": \"^2.0.19\",\n    \"@nextui-org/switch\": \"^2.0.21\",\n    \"@nextui-org/system\": \"^2.0.10\",\n    \"@nextui-org/theme\": \"^2.1.9\",\n    \"@nextui-org/tooltip\": \"^2.0.24\",\n    \"@radix-ui/react-aspect-ratio\": \"^1.0.3\",\n    \"@radix-ui/react-dialog\": \"^1.0.5\",\n    \"@radix-ui/react-navigation-menu\": \"^1.1.4\",\n    \"@radix-ui/react-popover\": \"^1.0.7\",\n    \"@radix-ui/react-scroll-area\": \"^1.0.5\",\n    \"@radix-ui/react-select\": \"^2.0.0\",\n    \"@radix-ui/react-slider\": \"^1.1.2\",\n    \"@radix-ui/react-tabs\": \"^1.0.4\",\n    \"@react-aria/interactions\": \"^3.19.1\",\n    \"@react-hookz/web\": \"^23.1.0\",\n    \"@remix-pwa/cache\": \"^2.0.12\",\n    \"@remix-pwa/client\": \"^2.0.1\",\n    \"@remix-pwa/strategy\": \"^2.1.9\",\n    \"@remix-pwa/sw\": \"^2.1.12\",\n    \"@remix-run/css-bundle\": \"^2.1.0\",\n    \"@remix-run/node\": \"^2.1.0\",\n    \"@remix-run/react\": \"^2.1.0\",\n    \"@remix-run/router\": \"^1.10.0\",\n    \"@remix-run/server-runtime\": \"^2.1.0\",\n    \"@resvg/resvg-js\": \"^2.6.0\",\n    \"@savvywombat/tailwindcss-grid-areas\": \"^3.1.0\",\n    \"@supabase/supabase-js\": \"^2.0.4\",\n    \"@vercel/remix\": \"^2.1.0\",\n    \"artplayer\": \"^5.0.9\",\n    \"autoprefixer\": \"^10.4.16\",\n    \"cachified\": \"^3.5.4\",\n    \"clsx\": \"^2.0.0\",\n    \"crypto-js\": \"^4.1.1\",\n    \"dashjs\": \"^4.7.2\",\n    \"dayjs\": \"^1.11.10\",\n    \"framer-motion\": \"^10.16.4\",\n    \"hls.js\": \"^1.4.12\",\n    \"i18next\": \"^23.6.0\",\n    \"i18next-browser-languagedetector\": \"^7.1.0\",\n    \"i18next-fs-backend\": \"^2.2.0\",\n    \"i18next-http-backend\": \"^2.2.2\",\n    \"intl-parse-accept-language\": \"^1.0.0\",\n    \"is-ip\": \"^5.0.1\",\n    \"isbot\": \"^3.7.0\",\n    \"lottie-web\": \"^5.12.2\",\n    \"lru-cache\": \"^10.0.1\",\n    \"next-themes\": \"^0.2.1\",\n    \"node-persist\": \"^3.1.3\",\n    \"node-vibrant\": \"^3.1.6\",\n    \"nprogress\": \"^0.2.0\",\n    \"photoswipe\": \"^5.4.2\",\n    \"postcss\": \"^8.4.29\",\n    \"postcss-cli\": \"^10.1.0\",\n    \"react\": \"18.3.0-canary-db69f95e4-20231002\",\n    \"react-device-detect\": \"^2.2.3\",\n    \"react-dom\": \"18.3.0-canary-db69f95e4-20231002\",\n    \"react-i18next\": \"^13.3.1\",\n    \"react-photoswipe-gallery\": \"^2.2.7\",\n    \"react-wrap-balancer\": \"^1.1.0\",\n    \"react-youtube\": \"^10.1.0\",\n    \"remix\": \"2.1.0\",\n    \"remix-i18next\": \"^5.4.0\",\n    \"remix-image\": \"^1.4.0\",\n    \"remix-image-sharp\": \"^0.1.4\",\n    \"remix-utils\": \"7.1.0\",\n    \"satori\": \"^0.10.9\",\n    \"sharp\": \"^0.31.0\",\n    \"sonner\": \"^1.0.3\",\n    \"swiper\": \"^10.3.1\",\n    \"tailwind-merge\": \"^1.14.0\",\n    \"tailwind-scrollbar-hide\": \"^1.1.7\",\n    \"tailwind-variants\": \"^0.1.14\",\n    \"tailwindcss\": \"^3.3.3\",\n    \"tailwindcss-animate\": \"^1.0.7\",\n    \"tiny-invariant\": \"^1.3.1\",\n    \"tinycolor2\": \"^1.6.0\",\n    \"vaul\": \"^0.7.5\",\n    \"web-push\": \"^3.6.6\",\n    \"zod\": \"^3.22.4\",\n    \"zustand\": \"^4.4.3\"\n  },\n  \"devDependencies\": {\n    \"@ianvs/prettier-plugin-sort-imports\": \"^4.1.0\",\n    \"@remix-pwa/dev\": \"^2.0.31\",\n    \"@remix-pwa/worker-runtime\": \"^2.0.8\",\n    \"@remix-run/dev\": \"^2.1.0\",\n    \"@remix-run/eslint-config\": \"^2.1.0\",\n    \"@remix-run/serve\": \"^2.1.0\",\n    \"@types/i18next-fs-backend\": \"^1.1.4\",\n    \"@types/node\": \"20.8.7\",\n    \"@types/node-persist\": \"^3.1.5\",\n    \"@types/nprogress\": \"^0.2.2\",\n    \"@types/react\": \"^18.2.31\",\n    \"@types/react-dom\": \"^18.2.14\",\n    \"@types/sharp\": \"^0.31.1\",\n    \"@types/tinycolor2\": \"^1.4.5\",\n    \"@types/web-push\": \"^3.6.2\",\n    \"@typescript-eslint/eslint-plugin\": \"^5.60.1\",\n    \"@typescript-eslint/parser\": \"^5.60.1\",\n    \"cross-env\": \"^7.0.3\",\n    \"dotenv\": \"^16.3.1\",\n    \"dotenv-cli\": \"^7.3.0\",\n    \"esbuild\": \"^0.19.5\",\n    \"eslint\": \"^8.41.0\",\n    \"eslint-config-prettier\": \"^8.8.0\",\n    \"eslint-plugin-jsx-a11y\": \"^6.7.1\",\n    \"eslint-plugin-prettier\": \"^4.2.1\",\n    \"eslint-plugin-react\": \"^7.33.2\",\n    \"eslint-plugin-react-hooks\": \"^4.6.0\",\n    \"eslint-plugin-tailwindcss\": \"^3.13.0\",\n    \"husky\": \"^8.0.3\",\n    \"lint-staged\": \"^13.0.1\",\n    \"npm-run-all\": \"^4.1.5\",\n    \"postcss-import\": \"^15.0.0\",\n    \"prettier\": \"^2.8.8\",\n    \"prettier-plugin-tailwindcss\": \"^0.3.0\",\n    \"prop-types\": \"^15.8.1\",\n    \"remix-development-tools\": \"^3.2.2\",\n    \"remix-flat-routes\": \"^0.6.1\",\n    \"remix-pwa\": \"^3.0.19\",\n    \"rimraf\": \"^5.0.5\",\n    \"typescript\": \"^5.2.2\"\n  },\n  \"lint-staged\": {\n    \"*.{tsx,ts,jsx,js,json,css,md}\": \"pnpm run prettier\",\n    \"*.{tsx,ts,jsx,js}\": \"pnpm run lint\"\n  },\n  \"engines\": {\n    \"node\": \">=18\"\n  },\n  \"pnpm\": {\n    \"peerDependencyRules\": {\n      \"allowedVersions\": {\n        \"remix\": \"2\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    'postcss-import': {},\n    'tailwindcss/nesting': {},\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n};\n"
  },
  {
    "path": "public/locales/en/auth.json",
    "content": "{\n  \"201-email\": \"Sign up successfully, please confirm your email before log in.\",\n  \"confirmPwd\": \"Confirm password\",\n  \"email\": \"Email\",\n  \"invalidEmail\": \"Please enter a valid email!\",\n  \"invalidInviteCode\": \"Invalid invitation code!\",\n  \"noInviteCode\": \"You must have an invitation code to sign up!\",\n  \"password\": \"Password\",\n  \"rememberMe\": \"Remember me\",\n  \"signIn\": \"Sign In\",\n  \"signUp\": \"Sign Up\",\n  \"unmatchedPassword\": \"Please enter your passwords correctly!\",\n  \"welcome\": \"Welcome to Remix Movie\"\n}\n"
  },
  {
    "path": "public/locales/en/common.json",
    "content": "{\n  \"airing\": \"Airing\",\n  \"airing-status\": \"Airing Status\",\n  \"airing-today-tv-shows\": \"Airing today - Tv shows\",\n  \"all\": \"All\",\n  \"also-known-as\": \"Also Known As\",\n  \"anime\": \"Anime\",\n  \"anime-genres\": \"Anime Genres\",\n  \"backdrops\": \"Backdrops\",\n  \"behind-the-scenes\": \"Behind the Scenes\",\n  \"biography\": \"Biography\",\n  \"birthday\": \"Birthday\",\n  \"bloopers\": \"Bloopers\",\n  \"budget\": \"Budget\",\n  \"cast\": \"Cast\",\n  \"character\": \"Character\",\n  \"characters\": \"Characters\",\n  \"clip\": \"Clip\",\n  \"country-origin\": \"Country of Origin\",\n  \"creators\": \"Creators\",\n  \"credits\": \"Credits\",\n  \"crew\": \"Crew\",\n  \"director\": \"Director\",\n  \"discover\": \"Discover\",\n  \"discover-anime\": \"Discover Anime\",\n  \"discover-movies\": \"Discover Movies\",\n  \"discover-tv\": \"Discover Tv Shows\",\n  \"duration\": \"Duration\",\n  \"end-date\": \"End Date\",\n  \"episode\": \"Episode\",\n  \"episode-duration\": \"Episode Duration\",\n  \"episodes\": \"Episodes\",\n  \"favourites_desc\": \"Favourites\",\n  \"featured-lists\": \"Featured Lists\",\n  \"featurette\": \"Featurette\",\n  \"filters\": \"Filters\",\n  \"first_air_date\": \"First Air Date\",\n  \"format\": \"Format\",\n  \"from\": \"From\",\n  \"gender\": \"Gender\",\n  \"genre\": \"Genre\",\n  \"genres\": \"Genres\",\n  \"grid\": \"Grid\",\n  \"home\": \"Home\",\n  \"japanese\": \"Japanese\",\n  \"keywords\": \"Keywords\",\n  \"keywords-helper\": \"Filter by keywords\",\n  \"known-for\": \"Known For\",\n  \"language\": \"Language\",\n  \"listType\": \"List Type\",\n  \"lists\": \"Lists\",\n  \"load-more\": \"Load More\",\n  \"login\": \"Login\",\n  \"logos\": \"Logos\",\n  \"logout\": \"Logout\",\n  \"media\": \"Media\",\n  \"min\": \"min\",\n  \"minimum-user-votes\": \"Minimum User Votes\",\n  \"more\": \"More\",\n  \"moreDetails\": \"More Details\",\n  \"movie-genres\": \"Movie Genres\",\n  \"movies\": \"Movies\",\n  \"network\": \"Network\",\n  \"no-results\": \"No Results\",\n  \"now-playing\": \"Now Playing\",\n  \"now-playing-movies\": \"Now Playing Movies\",\n  \"on-the-air-tv-shows\": \"On the air - Tv shows\",\n  \"opening-credits\": \"Opening Credits\",\n  \"original-language\": \"Original Language\",\n  \"original-title\": \"Original Title\",\n  \"original_title\": \"Name\",\n  \"overview\": \"Overview\",\n  \"people\": \"People\",\n  \"personal-info\": \"Personal Info\",\n  \"photos\": \"Photos\",\n  \"place-of-birth\": \"Place of Birth\",\n  \"popular\": \"Popular\",\n  \"popular-anime\": \"Popular Anime\",\n  \"popular-movies\": \"Popular Movies\",\n  \"popular-people\": \"Popular People\",\n  \"popular-tv-shows\": \"Popular Tv shows\",\n  \"popularity\": \"Popularity\",\n  \"popularity.asc\": \"Popularity Ascending\",\n  \"popularity.desc\": \"Popularity Descending\",\n  \"popularity_desc\": \"Popularity\",\n  \"posters\": \"Posters\",\n  \"primary_release_date.asc\": \"Release Date Ascending\",\n  \"primary_release_date.desc\": \"Release Date Descending\",\n  \"production-companies\": \"Production Companies\",\n  \"production-countries\": \"Production Countries\",\n  \"profiles\": \"Profiles\",\n  \"rated\": \"Rated\",\n  \"recent-episodes\": \"Recent Episodes\",\n  \"recommendations\": \"Recommendations\",\n  \"relations\": \"Relations\",\n  \"release-dates\": \"Release Dates\",\n  \"release_date\": \"Release Date\",\n  \"reset\": \"Reset\",\n  \"reset-tooltip\": \"Reset all filters\",\n  \"revenue\": \"Revenue\",\n  \"runtime\": \"Runtime (In Minutes)\",\n  \"score_desc\": \"Average Score\",\n  \"search\": {\n    \"action\": \"Search\",\n    \"helper\": {\n      \"anime\": \"Enter anime name and search\",\n      \"movie\": \"Enter movie name and search\",\n      \"people\": \"Enter people name and search\",\n      \"tv\": \"Enter tv show title and search\"\n    },\n    \"placeHolder\": {\n      \"anime\": \"Anime\",\n      \"movie\": \"Movie name\",\n      \"people\": \"People\",\n      \"tv\": \"TV show title\"\n    },\n    \"searchResults\": \"Search Results\",\n    \"title\": {\n      \"anime\": \"Search Anime\",\n      \"movie\": \"Search Movies\",\n      \"people\": \"Search People\",\n      \"tv\": \"Search TV Shows\"\n    }\n  },\n  \"season\": \"Season\",\n  \"seasons\": \"Seasons\",\n  \"settings\": \"Settings\",\n  \"show-hide-filter\": \"Show/Hide Filter\",\n  \"similar\": \"Similar\",\n  \"similar-movies\": \"Similar Movies\",\n  \"similar-tv-shows\": \"Similar Tv Shows\",\n  \"sort-by\": \"Sort By\",\n  \"sortBy\": \"Sort By\",\n  \"spoken-languages\": \"Spoken Languages\",\n  \"staff\": \"Staff\",\n  \"start-date\": \"Start Date\",\n  \"start_date_desc\": \"Release Date\",\n  \"status\": \"Status\",\n  \"studios\": \"Studios\",\n  \"synonyms\": \"Synonyms\",\n  \"table\": \"Table\",\n  \"teaser\": \"Teaser\",\n  \"title\": \"Sora\",\n  \"title.asc\": \"Title (A-Z)\",\n  \"title.desc\": \"Title (Z-A)\",\n  \"title_english\": \"Title (English)\",\n  \"title_romaji\": \"Title (Romaji)\",\n  \"to\": \"To\",\n  \"today\": \"Today\",\n  \"today-trending\": \"Trending Today\",\n  \"top-cast\": \"Top Cast\",\n  \"top-rated\": \"Top Rated\",\n  \"top-rated-movies\": \"Top Rated Movies\",\n  \"top-rated-tv-shows\": \"Top rated - Tv shows\",\n  \"trailer\": \"Trailer\",\n  \"trending\": {\n    \"all\": {\n      \"day\": \"Trending All Today\",\n      \"week\": \"Trending All This Week\"\n    },\n    \"movie\": {\n      \"day\": \"Trending Movies Today\",\n      \"week\": \"Trending Movies This Week\"\n    },\n    \"people\": {\n      \"day\": \"Trending People Today\",\n      \"week\": \"Trending People This Week\"\n    },\n    \"title\": \"Trending\",\n    \"tv\": {\n      \"day\": \"Trending TV Shows Today\",\n      \"week\": \"Trending TV Shows This Week\"\n    }\n  },\n  \"trending-all\": \"Trending\",\n  \"trending-anime\": \"Trending Anime\",\n  \"trending-movies\": \"Trending Movies\",\n  \"trending-people\": \"Trending People\",\n  \"trending-tv-shows\": \"Trending Tv Shows\",\n  \"trending_desc\": \"Trending\",\n  \"tv-show-genres\": \"TV Show Genres\",\n  \"tv-shows\": \"TV Shows\",\n  \"type\": \"Type\",\n  \"upcoming\": \"Upcoming\",\n  \"upcoming-movies\": \"Upcoming Movies\",\n  \"user-score\": \"User Score\",\n  \"userScore\": \"User Score\",\n  \"videos\": \"Videos\",\n  \"view-more\": \"View more\",\n  \"vote_average\": \"Vote Average\",\n  \"vote_average.asc\": \"Vote Average Ascending\",\n  \"vote_average.desc\": \"Vote Average Descending\",\n  \"watch\": \"Watch\",\n  \"watch-history\": \"Watch History\",\n  \"watch-now\": \"Watch Now\",\n  \"watch-trailer\": \"Watch Trailer\",\n  \"watchTrailer\": \"Watch Trailer\",\n  \"week\": \"This Week\",\n  \"week-trending\": \"Trending This Week\",\n  \"year\": \"Year\"\n}\n"
  },
  {
    "path": "public/locales/en/discover.json",
    "content": "{\n  \"anime\": \"Anime\",\n  \"anime-genres\": \"Anime Genres\",\n  \"categories\": \"Categories\",\n  \"collections\": \"Collections\",\n  \"discover\": \"Discover\",\n  \"featured-lists\": \"Featured Lists\",\n  \"filter\": \"Filter\",\n  \"general\": \"General\",\n  \"movie-genres\": \"Movies Genres\",\n  \"movies\": \"Movies\",\n  \"people\": \"People\",\n  \"search\": \"Search\",\n  \"trending\": \"Trending\",\n  \"tv-show-genres\": \"TV Shows Genres\",\n  \"tv-shows\": \"TV Shows\"\n}\n"
  },
  {
    "path": "public/locales/en/genres.json",
    "content": "{\n  \"anime-genres\": \"Anime Genres\",\n  \"movie-genres\": \"Movie Genres\",\n  \"tv-show-genres\": \"TV Show Genres\"\n}\n"
  },
  {
    "path": "public/locales/en/header.json",
    "content": "{\n  \"acid\": \"Acid\",\n  \"aqua\": \"Aqua\",\n  \"autumn\": \"Autumn\",\n  \"blue\": \"Blue\",\n  \"bumblebee\": \"Bumblebee\",\n  \"business\": \"Business\",\n  \"cmyk\": \"Cmyk\",\n  \"coffee\": \"Coffee\",\n  \"corporate\": \"Corporate\",\n  \"cupcake\": \"Cupcake\",\n  \"cyan\": \"Cyan\",\n  \"cyberpunk\": \"Cyberpunk\",\n  \"dark\": \"Dark\",\n  \"display\": \"Display\",\n  \"dracula\": \"Dracula\",\n  \"emerald\": \"Emerald\",\n  \"en\": \"English\",\n  \"fantasy\": \"Fantasy\",\n  \"forest\": \"Forest\",\n  \"fr\": \"French\",\n  \"garden\": \"Garden\",\n  \"green\": \"Green\",\n  \"halloween\": \"Halloween\",\n  \"language\": \"Language\",\n  \"lemonade\": \"Lemonade\",\n  \"light\": \"Light\",\n  \"lofi\": \"Lofi\",\n  \"log-out\": \"Log Out\",\n  \"luxury\": \"Luxury\",\n  \"night\": \"Night\",\n  \"pastel\": \"Pastel\",\n  \"pink\": \"Pink\",\n  \"purple\": \"Purple\",\n  \"red\": \"Red\",\n  \"retro\": \"Retro\",\n  \"sign-in\": \"Sign In\",\n  \"sign-up\": \"Sign Up\",\n  \"synthwave\": \"Synthwave\",\n  \"system\": \"System Default\",\n  \"theme\": \"Theme\",\n  \"theme-color\": \"Theme color\",\n  \"valentine\": \"Valentine\",\n  \"vi\": \"Vietnamese\",\n  \"winter\": \"Winter\",\n  \"wireframe\": \"Wireframe\",\n  \"yellow\": \"Yellow\"\n}\n"
  },
  {
    "path": "public/locales/en/settings.json",
    "content": "{\n  \"about\": \"About\",\n  \"account\": \"Account\",\n  \"acid\": \"Acid\",\n  \"appearance\": \"Appearance\",\n  \"aqua\": \"Aqua\",\n  \"auto-mini\": \"Auto mini\",\n  \"auto-mini-subtitle\": \"When the player scrolls outside the viewport, it automatically enters the mini mode.\",\n  \"auto-play-next-episode\": \"Auto play next episode\",\n  \"auto-play-next-episode-subtitle\": \"Whether to automatically play the next episode.\",\n  \"auto-playback\": \"Auto playback\",\n  \"auto-playback-subtitle\": \"Whether to use automatic playback function.\",\n  \"auto-size\": \"Auto size\",\n  \"auto-size-subtitle\": \"Whether to automatically adjust the size of the player to fit the video.\",\n  \"auto-skip-op-ed\": \"Auto skip OP/ED (Anime only)\",\n  \"auto-skip-op-ed-subtitle\": \"Whether to automatically skip the opening and ending credits.\",\n  \"auto-switch-subtitle\": \"Auto switch subtitle\",\n  \"auto-switch-subtitle-subtitle\": \"Whether to automatically switch the subtitle after adding subtitle to the list.\",\n  \"autoplay\": \"Autoplay\",\n  \"autoplay-subtitle\": \"Whether to play automatically.\",\n  \"autumn\": \"Autumn\",\n  \"blue\": \"Blue\",\n  \"bumblebee\": \"Bumblebee\",\n  \"business\": \"Business\",\n  \"card\": \"Card\",\n  \"cmyk\": \"Cmyk\",\n  \"coffee\": \"Coffee\",\n  \"corporate\": \"Corporate\",\n  \"cupcake\": \"Cupcake\",\n  \"custom-theme-color\": \"Custom theme color\",\n  \"cyan\": \"Cyan\",\n  \"cyberpunk\": \"Cyberpunk\",\n  \"dark-mode\": \"Dark mode\",\n  \"defaults\": \"Defaults\",\n  \"defaults-subtitle\": \"Defaults settings for the player. You may need to reload the player to apply the changes.\",\n  \"detail\": \"Detail\",\n  \"dracula\": \"Dracula\",\n  \"emerald\": \"Emerald\",\n  \"en\": \"English\",\n  \"experiments\": \"Experiments\",\n  \"experiments-subtitle\": \"This is a collection of experiments we're working on that might turn out to be useful.\",\n  \"fantasy\": \"Fantasy\",\n  \"fast-forward\": \"Fast forward\",\n  \"fast-forward-10s\": \"Fast forward 10s\",\n  \"fast-forward-5s\": \"Fast forward 5s\",\n  \"fast-forward-subtitle\": \"Long press video to fast forward on mobile\",\n  \"fast-rewind-10s\": \"Fast backward 10s\",\n  \"fast-rewind-5s\": \"Fast backward 5s\",\n  \"forest\": \"Forest\",\n  \"fr\": \"French\",\n  \"garden\": \"Garden\",\n  \"general\": \"General\",\n  \"gestures\": \"Gestures\",\n  \"gestures-subtitle\": \"Gestures settings for the player.\",\n  \"green\": \"Green\",\n  \"halloween\": \"Halloween\",\n  \"header\": \"Header\",\n  \"header-subtitle\": \"Header settings customization\",\n  \"infinite-scroll\": \"Infinite scroll\",\n  \"keyboard\": \"Keyboard Shortcuts\",\n  \"keyboard-subtitle\": \"Keyboard shortcuts for the player.\",\n  \"language\": \"Language\",\n  \"layout\": \"Layout\",\n  \"layout-subtitle\": \"Customize your application layout and style.\",\n  \"lemonade\": \"Lemonade\",\n  \"light-dark-only\": \"Light & dark only\",\n  \"list-loading-type\": \"List loading type\",\n  \"list-view-type\": \"List view type\",\n  \"lofi\": \"Lofi\",\n  \"loop\": \"Loop\",\n  \"loop-subtitle\": \"Whether to play in a loop.\",\n  \"luxury\": \"Luxury\",\n  \"media-list-banner\": \"Media list banner\",\n  \"media-list-banner-subtitle\": \"Media list banner settings customization\",\n  \"media-list-grid\": \"Media list grid\",\n  \"media-list-grid-subtitle\": \"Media list grid settings customization\",\n  \"mini-progressbar\": \"Mini progress bar\",\n  \"mini-progressbar-subtitle\": \"Mini progress bar, only appears when the player loses focus and is playing.\",\n  \"mute-trailer\": \"Mute trailer preview\",\n  \"mute-unmute\": \"Mute/unmute the video.\",\n  \"muted\": \"Muted\",\n  \"muted-subtitle\": \"Mute the player by default.\",\n  \"night\": \"Night\",\n  \"or\": \"or\",\n  \"pagination\": \"Pagination\",\n  \"pastel\": \"Pastel\",\n  \"pic-in-pic\": \"Picture in picture\",\n  \"pic-in-pic-subtitle\": \"Whether to display the picture in picture switch button in the bottom control bar.\",\n  \"pink\": \"Pink\",\n  \"play-trailer\": \"Play trailer preview\",\n  \"player\": \"Player\",\n  \"player-features\": \"Player features\",\n  \"player-features-subtitle\": \"Player features settings for the player. You may need to reload the player to apply the changes.\",\n  \"purple\": \"Purple\",\n  \"red\": \"Red\",\n  \"retro\": \"Retro\",\n  \"screenshot\": \"Screenshot\",\n  \"screenshot-subtitle\": \"Whether to display the screenshot button in the bottom control bar.\",\n  \"seek-to-end\": \"Seek to the last seconds of the video.\",\n  \"seek-to-percent\": \"Seek to the 10% to 90% of the video.\",\n  \"seek-to-start\": \"Seek to the beginning of the video.\",\n  \"settings\": \"Settings\",\n  \"show-breadcrumb\": \"Show breadcrumb\",\n  \"show-logo\": \"Show logo if available\",\n  \"show-skip-op-ed-button\": \"Show skip OP/ED button (Anime only)\",\n  \"show-skip-op-ed-button-subtitle\": \"Whether to display the skip OP/ED button.\",\n  \"show-spotlight\": \"Show spotlight\",\n  \"show-subtitle\": \"Show subtitle\",\n  \"show-subtitle-subtitle\": \"Whether to display the subtitle by default.\",\n  \"show-top-pagination\": \"Show top pagination\",\n  \"sidebar\": \"Sidebar\",\n  \"sidebar-boxed-mode\": \"Boxed\",\n  \"sidebar-hover-mode\": \"Hover\",\n  \"sidebar-mini-mode\": \"Mini\",\n  \"sidebar-mode\": \"Sidebar mode\",\n  \"sidebar-sheet-mode\": \"Sheet\",\n  \"sidebar-subtitle\": \"Sidebar settings customization\",\n  \"subtitle-background-color\": \"Subtitle background color\",\n  \"subtitle-background-opacity\": \"Subtitle background opacity\",\n  \"subtitle-font-color\": \"Subtitle font color\",\n  \"subtitle-font-size\": \"Subtitle font size\",\n  \"subtitle-text-effects\": \"Subtitle text effect\",\n  \"subtitle-window-color\": \"Subtitle window color\",\n  \"subtitle-window-opacity\": \"Subtitle window opacity\",\n  \"subtitles\": \"Subtitles\",\n  \"subtitles-subtitle\": \"Subtitles settings for the player.\",\n  \"swipe-to-fullscreen\": \"Swipe to fullscreen\",\n  \"swipe-to-fullscreen-subtitle\": \"Whether to enable the swipe gesture to fullscreen.\",\n  \"swipe-to-seek\": \"Swipe to seek\",\n  \"swipe-to-seek-subtitle\": \"Whether to enable the swipe gesture to seek.\",\n  \"synthwave\": \"Synthwave\",\n  \"table\": \"Table\",\n  \"theme\": \"Theme\",\n  \"theme-color\": \"Theme color\",\n  \"theme-subtitle\": \"Customize your aplication theme.\",\n  \"toggle-fullscreen\": \"Activate full screen. If full screen mode is enabled, activate F again or press escape to exit full screen mode.\",\n  \"toggle-play-pause\": \"Toggle play/pause\",\n  \"toggle-subtitle\": \"Activate subtitles if available. To hide subtitles, activate C again.\",\n  \"valentine\": \"Valentine\",\n  \"vi\": \"Vietnamese\",\n  \"volume-down\": \"Volume down\",\n  \"volume-up\": \"Volume up\",\n  \"winter\": \"Winter\",\n  \"wireframe\": \"Wireframe\",\n  \"yellow\": \"Yellow\"\n}\n"
  },
  {
    "path": "public/locales/en/sidebar.json",
    "content": "{\n  \"anime\": \"Anime\",\n  \"anime-discover\": \"Discover\",\n  \"anime-footer\": \"Anime\",\n  \"anime-popular\": \"Popular\",\n  \"anime-popular-subtitle\": \"Widely watched and buzzed-about anime\",\n  \"anime-random\": \"Random Anime\",\n  \"anime-random-subtitle\": \"Randomly selected anime without any filters\",\n  \"anime-recent-episodes\": \"Recent Episodes\",\n  \"anime-recent-episodes-subtitle\": \"Latest episodes of ongoing anime series\",\n  \"anime-trending\": \"Trending\",\n  \"anime-trending-subtitle\": \"Currently gaining popularity et attention among viewers\",\n  \"collections\": \"Collections\",\n  \"discover\": \"Discover\",\n  \"history\": \"History\",\n  \"home\": \"Home\",\n  \"movies\": \"Movies\",\n  \"movies-discover\": \"Discover\",\n  \"movies-footer\": \"Movies\",\n  \"movies-now-playing\": \"Now Playing\",\n  \"movies-now-playing-subtitle\": \"Currently showing in theaters\",\n  \"movies-popular\": \"Popular\",\n  \"movies-popular-subtitle\": \"Widely watched and buzzed-about films\",\n  \"movies-top-rated\": \"Top Rated\",\n  \"movies-top-rated-subtitle\": \"Highest rated films, based on viewers ratings and critic reviews\",\n  \"movies-upcoming\": \"Upcoming\",\n  \"movies-upcoming-subtitle\": \"Releases coming soon to theaters\",\n  \"people\": \"People\",\n  \"search\": \"Search\",\n  \"search-anime\": \"Search Anime\",\n  \"search-movies\": \"Search Movies\",\n  \"search-people\": \"Search People\",\n  \"search-tv-shows\": \"Search TV Shows\",\n  \"settings\": \"Settings\",\n  \"trending\": \"Trending\",\n  \"tv-shows\": \"TV Shows\",\n  \"tv-shows-airing-today\": \"Airing Today\",\n  \"tv-shows-airing-today-subtitle\": \"Currently playing on TV networks or streaming services\",\n  \"tv-shows-discover\": \"Discover\",\n  \"tv-shows-footer\": \"TV Shows\",\n  \"tv-shows-on-the-air\": \"On TV\",\n  \"tv-shows-on-the-air-subtitle\": \"Currently available to watch\",\n  \"tv-shows-popular\": \"Popular\",\n  \"tv-shows-popular-subtitle\": \"Widely watched and buzzed-about TV shows\",\n  \"tv-shows-top-rated\": \"Top Rated\",\n  \"tv-shows-top-rated-subtitle\": \"Highest rated TV shows, based on viewers ratings and critic reviews\",\n  \"featured-lists\": \"Featured Lists\"\n}\n"
  },
  {
    "path": "public/locales/fr/auth.json",
    "content": "{\n  \"201-email\": \"Votre compte a été bien créé, merci de confirmer votre l'adresse mail avant de vous connectez.\",\n  \"confirmPwd\": \"Confirmer le mot de passe\",\n  \"email\": \"Email\",\n  \"invalidEmail\": \"Merci d'entrer une valide email!\",\n  \"invalidInviteCode\": \"Code d'invitation invalide!\",\n  \"noInviteCode\": \"Vous devez avoir un code d'invitation pour vous inscrire!\",\n  \"password\": \"Mot de passe\",\n  \"rememberMe\": \"Se souvenir de moi\",\n  \"signIn\": \"Connectez-vous\",\n  \"signUp\": \"Inscrivez-vous\",\n  \"unmatchedPassword\": \"Merci d'entrer votre mot de passe correctement!\",\n  \"welcome\": \"Bienvenue à Remix Movie\"\n}\n"
  },
  {
    "path": "public/locales/fr/common.json",
    "content": "{\n  \"airing\": \"En cours de diffusion\",\n  \"airing-status\": \"Statut\",\n  \"airing-today-tv-shows\": \"Séries TV - En cours de diffusion aujourd'hui\",\n  \"all\": \"Tous\",\n  \"also-known-as\": \"Aussi connu sous le nom de\",\n  \"anime\": \"Anime\",\n  \"anime-genres\": \"Genres d'anime\",\n  \"backdrops\": \"Arrière-plans\",\n  \"behind-the-scenes\": \"Dans les coulisses\",\n  \"biography\": \"Biographie\",\n  \"birthday\": \"Anniversaire\",\n  \"bloopers\": \"Bêtisier\",\n  \"budget\": \"Budget\",\n  \"cast\": \"Distribution\",\n  \"character\": \"Personnage\",\n  \"characters\": \"Personnages\",\n  \"clip\": \"Clip\",\n  \"country-origin\": \"Pays d'origine\",\n  \"creators\": \"Créateurs\",\n  \"credits\": \"Crédits\",\n  \"crew\": \"Équipe\",\n  \"director\": \"Directeur\",\n  \"discover\": \"Découvrir\",\n  \"discover-anime\": \"Découvrir les animes\",\n  \"discover-movies\": \"Découvrir les films\",\n  \"discover-tv\": \"Découvrir les séries TV\",\n  \"duration\": \"Durée\",\n  \"end-date\": \"Date de fin\",\n  \"episode\": \"Épisode\",\n  \"episode-duration\": \"Durée de l'épisode\",\n  \"episodes\": \"Épisodes\",\n  \"favourites_desc\": \"Favoris\",\n  \"featured-lists\": \"Listes en vedette\",\n  \"featurette\": \"Featurette\",\n  \"filters\": \"Filtres\",\n  \"first_air_date\": \"Première diffusion\",\n  \"format\": \"Format\",\n  \"from\": \"De\",\n  \"gender\": \"Sexe\",\n  \"genre\": \"Genre\",\n  \"genres\": \"Genres\",\n  \"grid\": \"Grille\",\n  \"home\": \"Accueil\",\n  \"japanese\": \"Japonais\",\n  \"keywords\": \"Mots-clés\",\n  \"keywords-helper\": \"Filtrer par mots clés\",\n  \"known-for\": \"Connu pour\",\n  \"language\": \"Langue\",\n  \"listType\": \"Type de liste\",\n  \"lists\": \"Lists\",\n  \"load-more\": \"Charger plus\",\n  \"login\": \"Se connecter\",\n  \"logos\": \"Logos\",\n  \"logout\": \"Se déconnecter\",\n  \"media\": \"Médias\",\n  \"min\": \"min\",\n  \"minimum-user-votes\": \"Votes minimums\",\n  \"more\": \"Plus\",\n  \"moreDetails\": \"Plus de détails\",\n  \"movie-genres\": \"Genres de films\",\n  \"movies\": \"Films\",\n  \"network\": \"Réseau\",\n  \"no-results\": \"Aucun résultat\",\n  \"now-playing\": \"En cours de diffusion\",\n  \"now-playing-movies\": \"Films en cours de diffusion\",\n  \"on-the-air-tv-shows\": \"Séries TV - En cours de diffusion\",\n  \"opening-credits\": \"Générique de début\",\n  \"original-language\": \"Langue originale\",\n  \"original-title\": \"Titre original\",\n  \"original_title\": \"Nom\",\n  \"overview\": \"Aperçu\",\n  \"people\": \"Personnes\",\n  \"personal-info\": \"Informations personnelles\",\n  \"photos\": \"Photos\",\n  \"place-of-birth\": \"Lieu de naissance\",\n  \"popular\": \"Populaire\",\n  \"popular-anime\": \"Anime populaire\",\n  \"popular-movies\": \"Films populaires\",\n  \"popular-people\": \"Personnes populaires\",\n  \"popular-tv-shows\": \"Séries TV populaires\",\n  \"popularity\": \"Popularité\",\n  \"popularity.asc\": \"Popularité Ascendante\",\n  \"popularity.desc\": \"Popularité Descendante\",\n  \"popularity_desc\": \"Popularité\",\n  \"posters\": \"Affiches\",\n  \"primary_release_date.asc\": \"Date de sortie Ascendante\",\n  \"primary_release_date.desc\": \"Date de sortie Descendante\",\n  \"production-companies\": \"Production Companies\",\n  \"production-countries\": \"Production Countries\",\n  \"profiles\": \"Profils\",\n  \"rated\": \"Évalué\",\n  \"recent-episodes\": \"Épisodes récents\",\n  \"recommendations\": \"Recommandations\",\n  \"relations\": \"Relations\",\n  \"release-dates\": \"Dates de sortie\",\n  \"release_date\": \"Date de sortie\",\n  \"reset\": \"Réinitialiser\",\n  \"reset-tooltip\": \"Réinitialiser les filtres\",\n  \"revenue\": \"Revenue\",\n  \"runtime\": \"Durée (en minutes)\",\n  \"score_desc\": \"Note de l'utilisateur\",\n  \"search\": {\n    \"action\": \"Recherchez\",\n    \"helper\": {\n      \"anime\": \"Entrez le nom de l'animé et recherchez\",\n      \"movie\": \"Entrez le nom du film et recherchez\",\n      \"people\": \"Entrez le nom de la personne et recherchez\",\n      \"tv\": \"Entrez le titre du TV et recherchez\"\n    },\n    \"placeHolder\": {\n      \"anime\": \"Anime\",\n      \"movie\": \"Nom du film\",\n      \"people\": \"Personne\",\n      \"tv\": \"Titre du TV\"\n    },\n    \"searchResults\": \"Résultats de recherche\",\n    \"title\": {\n      \"anime\": \"Recherche d'animé\",\n      \"movie\": \"Recherche de films\",\n      \"people\": \"Recherche de personnes\",\n      \"tv\": \"Recherche de TVs\"\n    }\n  },\n  \"season\": \"Saison\",\n  \"seasons\": \"Saisons\",\n  \"settings\": \"Paramètres\",\n  \"show-hide-filter\": \"Afficher / Masquer le filtre\",\n  \"similar\": \"Similaire\",\n  \"similar-movies\": \"Films similaires\",\n  \"similar-tv-shows\": \"Séries TV similaires\",\n  \"sort-by\": \"Trier par\",\n  \"sortBy\": \"Trier par\",\n  \"spoken-languages\": \"Langues parlées\",\n  \"staff\": \"Staff\",\n  \"start-date\": \"Date de début\",\n  \"start_date_desc\": \"Date de début\",\n  \"status\": \"Statut\",\n  \"studios\": \"Studios\",\n  \"synonyms\": \"Synonymes\",\n  \"table\": \"Tableau\",\n  \"teaser\": \"Teaser\",\n  \"title\": \"Sora\",\n  \"title.asc\": \"Titre (A-Z)\",\n  \"title.desc\": \"Titre (Z-A)\",\n  \"title_english\": \"Titre (Anglais)\",\n  \"title_romaji\": \"Titre (Romaji)\",\n  \"to\": \"À\",\n  \"today\": \"Aujourd'hui\",\n  \"today-trending\": \"Tendance du jour\",\n  \"top-cast\": \"Top Cast\",\n  \"top-rated\": \"Les mieux notés\",\n  \"top-rated-movies\": \"Les films les mieux notés\",\n  \"top-rated-tv-shows\": \"Séries TV  - Les mieux notés\",\n  \"trailer\": \"Bande-annonce\",\n  \"trending\": {\n    \"all\": {\n      \"day\": \"Tendance du jour\",\n      \"week\": \"Tendance de la semaine\"\n    },\n    \"movie\": {\n      \"day\": \"Tendance des films aujourd'hui\",\n      \"week\": \"Tendance des films cette semaine\"\n    },\n    \"people\": {\n      \"day\": \"Tendance des personnes aujourd'hui\",\n      \"week\": \"Tendance des personnes cette semaine\"\n    },\n    \"title\": \"Tendance\",\n    \"tv\": {\n      \"day\": \"Tendance des séries TV aujourd'hui\",\n      \"week\": \"Tendance des séries TV cette semaine\"\n    }\n  },\n  \"trending-all\": \"Tendance\",\n  \"trending-anime\": \"Anime tendance\",\n  \"trending-movies\": \"Films tendance\",\n  \"trending-people\": \"Personnes tendance\",\n  \"trending-tv-shows\": \"Séries TV tendance\",\n  \"trending_desc\": \"Tendance\",\n  \"tv-show-genres\": \"Genres de séries TV\",\n  \"tv-shows\": \"Séries TV\",\n  \"type\": \"Type\",\n  \"upcoming\": \"À venir\",\n  \"upcoming-movies\": \"Films à venir\",\n  \"user-score\": \"Note des utilisateurs\",\n  \"userScore\": \"Note de l'utilisateur\",\n  \"videos\": \"Vidéos\",\n  \"view-more\": \"Voir plus\",\n  \"vote_average\": \"Vote moyen\",\n  \"vote_average.asc\": \"Vote Moyen Ascendant\",\n  \"vote_average.desc\": \"Vote Moyen Descendant\",\n  \"watch\": \"Regarder\",\n  \"watch-history\": \"Historique\",\n  \"watch-now\": \"Regarder maintenant\",\n  \"watch-trailer\": \"Regarder la bande-annonce\",\n  \"watchTrailer\": \"Regarder la bande-annonce\",\n  \"week\": \"Cette semaine\",\n  \"week-trending\": \"Tendance de la semaine\",\n  \"year\": \"Année\"\n}\n"
  },
  {
    "path": "public/locales/fr/discover.json",
    "content": "{\n  \"anime\": \"Anime\",\n  \"anime-genres\": \"Genres d'anime\",\n  \"categories\": \"Catégories\",\n  \"collections\": \"Collections\",\n  \"discover\": \"Découvrir\",\n  \"featured-lists\": \"Listes en vedette\",\n  \"filter\": \"Filtrer\",\n  \"general\": \"Général\",\n  \"movie-genres\": \"Genres de films\",\n  \"movies\": \"Films\",\n  \"people\": \"Célébrités\",\n  \"search\": \"Rechercher\",\n  \"trending\": \"Tendances\",\n  \"tv-show-genres\": \"Genres de séries TV\",\n  \"tv-shows\": \"Séries TV\"\n}\n"
  },
  {
    "path": "public/locales/fr/genres.json",
    "content": "{\n  \"anime-genres\": \"Genres d'anime\",\n  \"movie-genres\": \"Genres de films\",\n  \"tv-show-genres\": \"Genres de séries TV\"\n}\n"
  },
  {
    "path": "public/locales/fr/header.json",
    "content": "{\n  \"acid\": \"Acid\",\n  \"aqua\": \"Aqua\",\n  \"autumn\": \"Autumn\",\n  \"blue\": \"Bleu\",\n  \"bumblebee\": \"Bumblebee\",\n  \"business\": \"Business\",\n  \"cmyk\": \"Cmyk\",\n  \"coffee\": \"Coffee\",\n  \"corporate\": \"Corporate\",\n  \"cupcake\": \"Cupcake\",\n  \"cyan\": \"Cyan\",\n  \"cyberpunk\": \"Cyberpunk\",\n  \"dark\": \"Sombre\",\n  \"display\": \"Affichage\",\n  \"dracula\": \"Dracula\",\n  \"emerald\": \"Emerald\",\n  \"en\": \"Anglais\",\n  \"fantasy\": \"Fantasy\",\n  \"forest\": \"Forest\",\n  \"fr\": \"Français\",\n  \"garden\": \"Garden\",\n  \"green\": \"Vert\",\n  \"halloween\": \"Halloween\",\n  \"language\": \"Langue\",\n  \"lemonade\": \"Lemonade\",\n  \"light\": \"Clair\",\n  \"lofi\": \"Lofi\",\n  \"log-out\": \"Déconnexion\",\n  \"luxury\": \"Luxury\",\n  \"night\": \"Night\",\n  \"pastel\": \"Pastel\",\n  \"pink\": \"Rose\",\n  \"purple\": \"Violet\",\n  \"red\": \"Rouge\",\n  \"retro\": \"Retro\",\n  \"sign-in\": \"Connexion\",\n  \"sign-up\": \"Inscription\",\n  \"synthwave\": \"Synthwave\",\n  \"system\": \"Système par défaut\",\n  \"theme\": \"Thème\",\n  \"theme-color\": \"Couleur du thème\",\n  \"valentine\": \"Valentine\",\n  \"vi\": \"Vietnamien\",\n  \"winter\": \"Winter\",\n  \"wireframe\": \"Wireframe\",\n  \"yellow\": \"Jaune\"\n}\n"
  },
  {
    "path": "public/locales/fr/settings.json",
    "content": "{\n  \"about\": \"À propos\",\n  \"account\": \"Compte\",\n  \"acid\": \"Acid\",\n  \"appearance\": \"Apparence\",\n  \"aqua\": \"Aqua\",\n  \"auto-mini\": \"Mini automatique\",\n  \"auto-mini-subtitle\": \"When the player scrolls outside the viewport, it automatically enters the mini mode.\",\n  \"auto-play-next-episode\": \"Lecture automatique de l'épisode suivant\",\n  \"auto-play-next-episode-subtitle\": \"S'il faut jouer automatiquement le prochain épisode.\",\n  \"auto-playback\": \"Playback automatique\",\n  \"auto-playback-subtitle\": \"S'il faut utiliser la fonction de playback automatique.\",\n  \"auto-size\": \"Taille automatique\",\n  \"auto-size-subtitle\": \"S'il faut ajuster automatiquement la taille du lecteur pour s'adapter à la vidéo.\",\n  \"auto-skip-op-ed\": \"Auto skip op / ed (anime uniquement)\",\n  \"auto-skip-op-ed-subtitle\": \"S'il faut sauter automatiquement les crédits d'ouverture et de fin.\",\n  \"auto-switch-subtitle\": \"Changer automatiquement de sous-titre\",\n  \"auto-switch-subtitle-subtitle\": \"Changer automatiquement le sous-titre après avoir ajouté le sous-titre à la liste.\",\n  \"autoplay\": \"Lecture automatique\",\n  \"autoplay-subtitle\": \"Lancez le lecteur en mode lecture automatique.\",\n  \"autumn\": \"Autumn\",\n  \"blue\": \"Bleu\",\n  \"bumblebee\": \"Bumblebee\",\n  \"business\": \"Business\",\n  \"card\": \"Card\",\n  \"cmyk\": \"Cmyk\",\n  \"coffee\": \"Coffee\",\n  \"corporate\": \"Corporate\",\n  \"cupcake\": \"Cupcake\",\n  \"custom-theme-color\": \"Couleur du thème personnalisé\",\n  \"cyan\": \"Cyan\",\n  \"cyberpunk\": \"Cyberpunk\",\n  \"dark-mode\": \"Mode sombre\",\n  \"defaults\": \"Paramètres par défaut\",\n  \"defaults-subtitle\": \"Paramètres par défaut pour le lecteur. Vous devrez peut-être recharger le joueur pour appliquer les modifications.\",\n  \"detail\": \"Detail\",\n  \"dracula\": \"Dracula\",\n  \"emerald\": \"Emerald\",\n  \"en\": \"Anglais\",\n  \"experiments\": \"Expériences\",\n  \"experiments-subtitle\": \"Ceci est une collection d'expériences sur lesquelles nous travaillons qui pourraient s'avérer utiles.\",\n  \"fantasy\": \"Fantasy\",\n  \"fast-forward\": \"Avance rapide\",\n  \"fast-forward-10s\": \"Avance rapide de 10s\",\n  \"fast-forward-5s\": \"Avance rapide de 5s\",\n  \"fast-forward-subtitle\": \"Appuyez longuement sur la vidéo pour avancer rapidement sur mobile\",\n  \"fast-rewind-10s\": \"Retour rapide de 10s\",\n  \"fast-rewind-5s\": \"Retour rapide de 5s\",\n  \"forest\": \"Forest\",\n  \"fr\": \"Français\",\n  \"garden\": \"Garden\",\n  \"general\": \"Général\",\n  \"gestures\": \"Gestes\",\n  \"gestures-subtitle\": \"Paramètres pour les gestes\",\n  \"green\": \"Vert\",\n  \"halloween\": \"Halloween\",\n  \"header\": \"L'en-tête\",\n  \"header-subtitle\": \"Personnalisation des paramètres d'en-tête\",\n  \"infinite-scroll\": \"Infinite scroll\",\n  \"keyboard\": \"Clavier\",\n  \"keyboard-subtitle\": \"Paramètres pour le clavier\",\n  \"language\": \"Langue\",\n  \"layout\": \"Disposition\",\n  \"layout-subtitle\": \"Personnalisez la disposition et le style de votre application.\",\n  \"lemonade\": \"Lemonade\",\n  \"light-dark-only\": \"Clair et foncé uniquement\",\n  \"list-loading-type\": \"List loading type\",\n  \"list-view-type\": \"List view type\",\n  \"lofi\": \"Lofi\",\n  \"loop\": \"Boucle\",\n  \"loop-subtitle\": \"Lancez le lecteur en mode boucle.\",\n  \"luxury\": \"Luxury\",\n  \"media-list-banner\": \"Media list banner\",\n  \"media-list-banner-subtitle\": \"Media list banner settings customization\",\n  \"media-list-grid\": \"Media list grid\",\n  \"media-list-grid-subtitle\": \"Media list grid settings customization\",\n  \"mini-progressbar\": \"Barre de progression mini\",\n  \"mini-progressbar-subtitle\": \"Mini Progress Bar, n'apparaît que lorsque le joueur perd le foyer et joue.\",\n  \"mute-trailer\": \"Couper le son de la bande-annonce\",\n  \"mute-unmute\": \"Activer le mode muet. Si le mode muet est activé, activez à nouveau M.\",\n  \"muted\": \"Muet\",\n  \"muted-subtitle\": \"Lancez le lecteur en mode muet.\",\n  \"night\": \"Night\",\n  \"or\": \"ou\",\n  \"pagination\": \"Pagination\",\n  \"pastel\": \"Pastel\",\n  \"pic-in-pic\": \"Image dans l'image\",\n  \"pic-in-pic-subtitle\": \"Lancez le lecteur dans une fenêtre flottante.\",\n  \"pink\": \"Rose\",\n  \"play-trailer\": \"Lire la bande-annonce\",\n  \"player\": \"Lecteur\",\n  \"player-features\": \"Fonctionnalités du lecteur\",\n  \"player-features-subtitle\": \"Le joueur propose des paramètres pour le joueur. Vous devrez peut-être recharger le joueur pour appliquer les modifications.\",\n  \"purple\": \"Violet\",\n  \"red\": \"Rouge\",\n  \"retro\": \"Retro\",\n  \"screenshot\": \"Capture d'écran\",\n  \"screenshot-subtitle\": \"S'il faut afficher le bouton de capture d'écran dans la barre de commande inférieure.\",\n  \"seek-to-end\": \"Rechercher la fin de la vidéo.\",\n  \"seek-to-percent\": \"Rechercher entre 10% et 90% de la vidéo.\",\n  \"seek-to-start\": \"Rechercher le début de la vidéo.\",\n  \"settings\": \"Paramètres\",\n  \"show-breadcrumb\": \"Show breadcrumb\",\n  \"show-logo\": \"Afficher le logo si disponible\",\n  \"show-skip-op-ed-button\": \"Afficher le bouton de saut op / ed (anime uniquement)\",\n  \"show-skip-op-ed-button-subtitle\": \"S'il faut afficher le bouton de saut des crédits d'ouverture et de fin.\",\n  \"show-spotlight\": \"Afficher le projecteur\",\n  \"show-subtitle\": \"Afficher le sous-titre\",\n  \"show-subtitle-subtitle\": \"Whether to display the subtitle by default.\",\n  \"show-top-pagination\": \"Afficher la pagination en haut\",\n  \"sidebar\": \"Barre latérale\",\n  \"sidebar-boxed-mode\": \"Boxed\",\n  \"sidebar-hover-mode\": \"Hover\",\n  \"sidebar-mini-mode\": \"Mini\",\n  \"sidebar-mode\": \"Mode de barre latérale\",\n  \"sidebar-sheet-mode\": \"Sheet\",\n  \"sidebar-subtitle\": \"Personnalisation des paramètres de la barre latérale\",\n  \"subtitle-background-color\": \"Couleur de fond de sous-titre\",\n  \"subtitle-background-opacity\": \"Opacité du fond du sous-titre\",\n  \"subtitle-font-color\": \"Couleur de police sous-titre\",\n  \"subtitle-font-size\": \"Taille de la police sous-titre\",\n  \"subtitle-text-effects\": \"Effet de texte sous-titre\",\n  \"subtitle-window-color\": \"Couleur de fenêtre sous-titre\",\n  \"subtitle-window-opacity\": \"Opacité de la fenêtre de sous-titre\",\n  \"subtitles\": \"Sous-titres\",\n  \"subtitles-subtitle\": \"Paramètres par défaut pour les sous-titres.\",\n  \"swipe-to-fullscreen\": \"Glisser vers plein écran\",\n  \"swipe-to-fullscreen-subtitle\": \"S'il faut permettre le geste de balayage en plein écran.\",\n  \"swipe-to-seek\": \"Swipe to seek\",\n  \"swipe-to-seek-subtitle\": \"Whether to enable the swipe gesture to seek.\",\n  \"synthwave\": \"Synthwave\",\n  \"table\": \"Table\",\n  \"theme\": \"Thème\",\n  \"theme-color\": \"Couleur du thème\",\n  \"theme-subtitle\": \"Personnalisez le thème de votre application.\",\n  \"toggle-fullscreen\": \"Activer le plein écran. Si le mode plein écran est activé, activez à nouveau F ou appuyez sur Échap pour quitter le mode plein écran.\",\n  \"toggle-play-pause\": \"Basculer lecture/pause\",\n  \"toggle-subtitle\": \"Activez les sous-titres et les sous-titres si disponibles. Pour masquer les sous-titres, activez à nouveau C.\",\n  \"valentine\": \"Valentine\",\n  \"vi\": \"Vietnamien\",\n  \"volume-down\": \"Baisser le son\",\n  \"volume-up\": \"Monter le son\",\n  \"winter\": \"Winter\",\n  \"wireframe\": \"Wireframe\",\n  \"yellow\": \"Jaune\"\n}\n"
  },
  {
    "path": "public/locales/fr/sidebar.json",
    "content": "{\n  \"anime\": \"Anime\",\n  \"anime-discover\": \"Découvrir\",\n  \"anime-footer\": \"Anime\",\n  \"anime-popular\": \"Populaires\",\n  \"anime-popular-subtitle\": \"Anime les plus populaires\",\n  \"anime-random\": \"Aléatoire\",\n  \"anime-random-subtitle\": \"Anime sélectionné aléatoirement sans aucun filtre\",\n  \"anime-recent-episodes\": \"Derniers épisodes\",\n  \"anime-recent-episodes-subtitle\": \"Derniers épisodes de séries animées en cours\",\n  \"anime-trending\": \"Tendances\",\n  \"anime-trending-subtitle\": \"Anime actuellement en popularité et en attention auprès des téléspectateurs\",\n  \"collections\": \"Collections\",\n  \"discover\": \"Découvrir\",\n  \"history\": \"Historique\",\n  \"home\": \"Accueil\",\n  \"movies\": \"Films\",\n  \"movies-discover\": \"Découvrir\",\n  \"movies-footer\": \"Films\",\n  \"movies-now-playing\": \"En cours\",\n  \"movies-now-playing-subtitle\": \"Films en cours de diffusion au cinéma\",\n  \"movies-popular\": \"Populaires\",\n  \"movies-popular-subtitle\": \"Films les plus populaires\",\n  \"movies-top-rated\": \"Les mieux notées\",\n  \"movies-top-rated-subtitle\": \"Films les mieux notés, basés sur les notes des téléspectateurs et les critiques des critiques\",\n  \"movies-upcoming\": \"A venir\",\n  \"movies-upcoming-subtitle\": \"Sorties de films prochainement au cinéma\",\n  \"people\": \"Célébrités\",\n  \"search\": \"Rechercher\",\n  \"search-anime\": \"Rechercher des Animes\",\n  \"search-movies\": \"Rechercher des Films\",\n  \"search-people\": \"Rechercher des Personnes\",\n  \"search-tv-shows\": \"Rechercher des Séries TV\",\n  \"settings\": \"Paramètres\",\n  \"trending\": \"Tendances\",\n  \"tv-shows\": \"Séries TV\",\n  \"tv-shows-airing-today\": \"Diffusion aujourd'hui\",\n  \"tv-shows-airing-today-subtitle\": \"En cours de diffuser sur les réseaux de télévision ou les services de streaming\",\n  \"tv-shows-discover\": \"Découvrir\",\n  \"tv-shows-footer\": \"Séries TV\",\n  \"tv-shows-on-the-air\": \"Sur la TV\",\n  \"tv-shows-on-the-air-subtitle\": \"Actuellement disponible pour regarder\",\n  \"tv-shows-popular\": \"Populaires\",\n  \"tv-shows-popular-subtitle\": \"Séries TV les plus populaires\",\n  \"tv-shows-top-rated\": \"Les mieux notées\",\n  \"tv-shows-top-rated-subtitle\": \"Séries TV les mieux notées, basées sur les notes des téléspectateurs et les critiques des critiques\",\n  \"featured-lists\": \"Listes en vedette\"\n}\n"
  },
  {
    "path": "public/locales/vi/auth.json",
    "content": "{\n  \"201-email\": \"Đăng kí thành công. Nhớ xác nhận email của bạn trước khi đăng nhập nhé.\",\n  \"confirmPwd\": \"Xác nhận mật khẩu\",\n  \"email\": \"Email\",\n  \"invalidEmail\": \"Email của bạn không hợp lệ!\",\n  \"invalidInviteCode\": \"Mã giới thiệu không đúng!\",\n  \"noInviteCode\": \"Bạn cần phải có mã giới thiệu để đăng kí!\",\n  \"password\": \"Mật khẩu\",\n  \"rememberMe\": \"Ghi nhớ phiên\",\n  \"signIn\": \"Đăng nhập\",\n  \"signUp\": \"Đăng kí\",\n  \"unmatchedPassword\": \"Mật khẩu nhập chưa chính xác!\",\n  \"welcome\": \"Chào mừng bạn đến với  Remix Movie\"\n}\n"
  },
  {
    "path": "public/locales/vi/common.json",
    "content": "{\n  \"airing\": \"Đang phát sóng\",\n  \"airing-status\": \"Trạng thái phát sóng\",\n  \"airing-today-tv-shows\": \"Phim bộ đang phát sóng hôm nay\",\n  \"all\": \"Tất cả\",\n  \"also-known-as\": \"Tên khác\",\n  \"anime\": \"Anime\",\n  \"anime-genres\": \"Thể loại anime\",\n  \"backdrops\": \"Ảnh nền\",\n  \"behind-the-scenes\": \"Sau cánh gà\",\n  \"biography\": \"Tiểu sử\",\n  \"birthday\": \"Ngày sinh\",\n  \"bloopers\": \"Bloopers\",\n  \"budget\": \"Kinh phí\",\n  \"cast\": \"Diễn viên\",\n  \"character\": \"Nhân vật\",\n  \"characters\": \"Nhân vật\",\n  \"clip\": \"Clip\",\n  \"country-origin\": \"Quốc gia sản xuất\",\n  \"creators\": \"Người tạo ra\",\n  \"credits\": \"Tác giả\",\n  \"crew\": \"Nhân viên\",\n  \"director\": \"Đạo diễn\",\n  \"discover\": \"Khám phá\",\n  \"discover-anime\": \"Khám phá anime\",\n  \"discover-movies\": \"Khám phá phim lẻ\",\n  \"discover-tv\": \"Khám phá phim bộ\",\n  \"duration\": \"Thời lượng\",\n  \"end-date\": \"Ngày kết thúc\",\n  \"episode\": \"Tập\",\n  \"episode-duration\": \"Thời lượng tập\",\n  \"episodes\": \"Tập\",\n  \"favourites_desc\": \"Yêu thích\",\n  \"featured-lists\": \"Danh sách nổi bật\",\n  \"featurette\": \"Featurette\",\n  \"filters\": \"Bộ lọc\",\n  \"first_air_date\": \"Ngày công chiếu\",\n  \"format\": \"Định dạng\",\n  \"from\": \"Từ\",\n  \"gender\": \"Giới tính\",\n  \"genre\": \"Thể loại\",\n  \"genres\": \"Thể loại\",\n  \"grid\": \"Lưới\",\n  \"home\": \"Trang chủ\",\n  \"japanese\": \"Tiếng Nhật\",\n  \"keywords\": \"Từ khóa\",\n  \"keywords-helper\": \"Lọc theo từ khóa\",\n  \"known-for\": \"Nổi tiếng với\",\n  \"language\": \"Ngôn ngữ\",\n  \"listType\": \"Loại danh sách\",\n  \"lists\": \"Danh sách\",\n  \"load-more\": \"Tải thêm\",\n  \"login\": \"Đăng nhập\",\n  \"logos\": \"Logo\",\n  \"logout\": \"Đăng xuất\",\n  \"media\": \"Phương tiện\",\n  \"min\": \"phút\",\n  \"minimum-user-votes\": \"Số đánh giá tối thiểu\",\n  \"more\": \"Xem thêm\",\n  \"moreDetails\": \"Xem chi tiết\",\n  \"movie-genres\": \"Thể loại phim lẻ\",\n  \"movies\": \"Phim lẻ\",\n  \"network\": \"Mạng lưới phát sóng\",\n  \"no-results\": \"Không tìm thấy kết quả\",\n  \"now-playing\": \"Đang chiếu\",\n  \"now-playing-movies\": \"Phim lẻ đang chiếu\",\n  \"on-the-air-tv-shows\": \"Phim bộ đang chiếu\",\n  \"opening-credits\": \"Opening Credits\",\n  \"original-language\": \"Ngôn ngữ gốc\",\n  \"original-title\": \"Tên gốc\",\n  \"original_title\": \"Tên\",\n  \"overview\": \"Tổng quan\",\n  \"people\": \"Người nổi tiếng\",\n  \"personal-info\": \"Thông tin cá nhân\",\n  \"photos\": \"Ảnh\",\n  \"place-of-birth\": \"Nơi sinh\",\n  \"popular\": \"Phổ biến\",\n  \"popular-anime\": \"Anime được yêu thích\",\n  \"popular-movies\": \"Phim lẻ được yêu thích\",\n  \"popular-people\": \"Diễn viên nổi tiếng\",\n  \"popular-tv-shows\": \"Phim bộ được yêu thích\",\n  \"popularity\": \"Độ phổ biến\",\n  \"popularity.asc\": \"Độ phổ biến tăng dần\",\n  \"popularity.desc\": \"Độ phổ biến giảm dần\",\n  \"popularity_desc\": \"Độ phổ biến\",\n  \"posters\": \"Poster\",\n  \"primary_release_date.asc\": \"Ngày phát hành tăng dần\",\n  \"primary_release_date.desc\": \"Ngày phát hành giảm dần\",\n  \"production-companies\": \"Công ty sản xuất\",\n  \"production-countries\": \"Quốc gia sản xuất\",\n  \"profiles\": \"Hồ sơ\",\n  \"rated\": \"Xếp hạng\",\n  \"recent-episodes\": \"Tập mới nhất\",\n  \"recommendations\": \"Gợi ý\",\n  \"relations\": \"Liên quan\",\n  \"release-dates\": \"Ngày phát hành\",\n  \"release_date\": \"Ngày phát hành\",\n  \"reset\": \"Đặt lại\",\n  \"reset-tooltip\": \"Đặt lại tất cả các lựa chọn\",\n  \"revenue\": \"Doanh thu\",\n  \"runtime\": \"Thời lượng (Bằng phút)\",\n  \"score_desc\": \"Điểm đánh giá\",\n  \"search\": {\n    \"action\": \"Tìm kiếm\",\n    \"helper\": {\n      \"anime\": \"Nhập tên anime và tìm kiếm\",\n      \"movie\": \"Nhập tên phim và tìm kiếm\",\n      \"people\": \"Nhập tên diễn viên và tìm kiếm\",\n      \"tv\": \"Nhập tên phim và tìm kiếm\"\n    },\n    \"placeHolder\": {\n      \"anime\": \"Tên Anime\",\n      \"movie\": \"Tên phim\",\n      \"people\": \"Tên diễn viên\",\n      \"tv\": \"Tên phim\"\n    },\n    \"searchResults\": \"Kết quả tìm kiếm\",\n    \"title\": {\n      \"anime\": \"Tìm kiếm anime\",\n      \"movie\": \"Tìm kiếm phim lẻ\",\n      \"people\": \"Tìm kiếm diễn viên\",\n      \"tv\": \"Tìm kiếm phim bộ\"\n    }\n  },\n  \"season\": \"Mùa\",\n  \"seasons\": \"Mùa\",\n  \"settings\": \"Cài đặt\",\n  \"show-hide-filter\": \"Hiển thị / Ẩn bộ lọc\",\n  \"similar\": \"Tương tự\",\n  \"similar-movies\": \"Phim lẻ tương tự\",\n  \"similar-tv-shows\": \"Phim bộ tương tự\",\n  \"sort-by\": \"Sắp xếp theo\",\n  \"sortBy\": \"Xếp theo\",\n  \"spoken-languages\": \"Ngôn ngữ nói\",\n  \"staff\": \"Nhân viên\",\n  \"start-date\": \"Ngày bắt đầu\",\n  \"start_date_desc\": \"Ngày bắt đầu\",\n  \"status\": \"Trạng thái\",\n  \"studios\": \"Studio\",\n  \"synonyms\": \"Tên khác\",\n  \"table\": \"Bảng\",\n  \"teaser\": \"Teaser\",\n  \"title\": \"Sora\",\n  \"title.asc\": \"Tên (A-Z)\",\n  \"title.desc\": \"Tên (Z-A)\",\n  \"title_english\": \"Tên (Tiếng Anh)\",\n  \"title_romaji\": \"Tên (Tiếng Nhật)\",\n  \"to\": \"Đến\",\n  \"today\": \"Hôm nay\",\n  \"top-cast\": \"Diễn viên chính\",\n  \"top-rated\": \"Xếp hạng cao nhất\",\n  \"today-trending\": \"Xu hướng hôm nay\",\n  \"top-rated-movies\": \"Phim lẻ được xếp hạng cao nhất\",\n  \"top-rated-tv-shows\": \"Phim bộ được xếp hạng cao nhất\",\n  \"trailer\": \"Trailer\",\n  \"trending\": {\n    \"all\": {\n      \"day\": \"Xu hướng trong ngày\",\n      \"week\": \"Xu hướng trong tuần\"\n    },\n    \"movie\": {\n      \"day\": \"Xu hướng phim lẻ trong ngày\",\n      \"week\": \"Xu hướng phim lẻ trong tuần\"\n    },\n    \"people\": {\n      \"day\": \"Người nổi tiếng trong ngày\",\n      \"week\": \"Người nổi tiếng trong tuần\"\n    },\n    \"title\": \"Xu hướng\",\n    \"tv\": {\n      \"day\": \"Xu hướng phim bộ trong ngày\",\n      \"week\": \"Xu hướng phim bộ trong tuần\"\n    }\n  },\n  \"trending-all\": \"Xu hướng\",\n  \"trending-anime\": \"Anime xu hướng\",\n  \"trending-movies\": \"Phim lẻ xu hướng\",\n  \"trending-people\": \"Người nổi tiếng xu hướng\",\n  \"trending-tv-shows\": \"Phim bộ xu hướng\",\n  \"trending_desc\": \"Xu hướng\",\n  \"tv-show-genres\": \"Thể loại phim bộ\",\n  \"tv-shows\": \"Phim bộ\",\n  \"type\": \"Phân Loại\",\n  \"upcoming\": \"Sắp ra mắt\",\n  \"upcoming-movies\": \"Phim lẻ sắp ra mắt\",\n  \"user-score\": \"Điểm người dùng\",\n  \"userScore\": \"Người xem xếp hạng\",\n  \"videos\": \"Video\",\n  \"view-more\": \"Xem thêm\",\n  \"vote_average\": \"Điểm\",\n  \"vote_average.asc\": \"Điểm đánh giá tăng dần\",\n  \"vote_average.desc\": \"Điểm đánh giá giảm dần\",\n  \"watch\": \"Xem\",\n  \"watch-history\": \"Lịch sử xem\",\n  \"watch-now\": \"Xem ngay\",\n  \"watch-trailer\": \"Xem trailer\",\n  \"watchTrailer\": \"Xem Trailer\",\n  \"week\": \"Tuần này\",\n  \"week-trending\": \"Xu hướng tuần này\",\n  \"year\": \"Năm\"\n}\n"
  },
  {
    "path": "public/locales/vi/discover.json",
    "content": "{\n  \"anime\": \"Anime\",\n  \"anime-genres\": \"Thể loại anime\",\n  \"categories\": \"Danh mục\",\n  \"collections\": \"Bộ sưu tập\",\n  \"discover\": \"Khám phá\",\n  \"featured-lists\": \"Danh sách nổi bật\",\n  \"filter\": \"Lọc\",\n  \"general\": \"Tổng quan\",\n  \"movie-genres\": \"Thể loại phim lẻ\",\n  \"movies\": \"Phim lẻ\",\n  \"people\": \"Người nổi tiếng\",\n  \"search\": \"Tìm kiếm\",\n  \"trending\": \"Xu hướng\",\n  \"tv-show-genres\": \"Thể loại phim bộ\",\n  \"tv-shows\": \"Phim bộ\"\n}\n"
  },
  {
    "path": "public/locales/vi/genres.json",
    "content": "{\n  \"anime-genres\": \"Thể loại anime\",\n  \"movie-genres\": \"Thể loại phim lẻ\",\n  \"tv-show-genres\": \"Thể loại phim bộ\"\n}\n"
  },
  {
    "path": "public/locales/vi/header.json",
    "content": "{\n  \"acid\": \"Acid\",\n  \"aqua\": \"Aqua\",\n  \"autumn\": \"Autumn\",\n  \"blue\": \"Xanh dương\",\n  \"bumblebee\": \"Bumblebee\",\n  \"business\": \"Business\",\n  \"cmyk\": \"Cmyk\",\n  \"coffee\": \"Coffee\",\n  \"corporate\": \"Corporate\",\n  \"cupcake\": \"Cupcake\",\n  \"cyan\": \"Xanh lam\",\n  \"cyberpunk\": \"Cyberpunk\",\n  \"dark\": \"Tối\",\n  \"display\": \"Hiển thị\",\n  \"dracula\": \"Dracula\",\n  \"emerald\": \"Emerald\",\n  \"en\": \"Tiếng Anh\",\n  \"fantasy\": \"Fantasy\",\n  \"forest\": \"Forest\",\n  \"fr\": \"Tiếng Pháp\",\n  \"garden\": \"Garden\",\n  \"green\": \"Xanh lá cây\",\n  \"halloween\": \"Halloween\",\n  \"language\": \"Ngôn ngữ\",\n  \"lemonade\": \"Lemonade\",\n  \"light\": \"Sáng\",\n  \"lofi\": \"Lofi\",\n  \"log-out\": \"Đăng xuất\",\n  \"luxury\": \"Luxury\",\n  \"night\": \"Night\",\n  \"pastel\": \"Pastel\",\n  \"pink\": \"Hồng\",\n  \"purple\": \"Tím\",\n  \"red\": \"Đỏ\",\n  \"retro\": \"Retro\",\n  \"sign-in\": \"Đăng nhập\",\n  \"sign-up\": \"Đăng ký\",\n  \"synthwave\": \"Synthwave\",\n  \"system\": \"Mặc định hệ thống\",\n  \"theme\": \"Chủ đề\",\n  \"theme-color\": \"Màu chủ đề\",\n  \"valentine\": \"Valentine\",\n  \"vi\": \"Tiếng Việt\",\n  \"winter\": \"Winter\",\n  \"wireframe\": \"Wireframe\",\n  \"yellow\": \"Vàng\"\n}\n"
  },
  {
    "path": "public/locales/vi/settings.json",
    "content": "{\n  \"about\": \"Giới thiệu\",\n  \"account\": \"Tài khoản\",\n  \"acid\": \"Acid\",\n  \"appearance\": \"Giao diện\",\n  \"aqua\": \"Aqua\",\n  \"auto-mini\": \"Tự động chuyển sang chế độ nhỏ\",\n  \"auto-mini-subtitle\": \"Khi trình phát cuộn bên ngoài chế độ xem, nó sẽ tự động vào chế độ mini.\",\n  \"auto-play-next-episode\": \"Tự động phát tập tiếp theo\",\n  \"auto-play-next-episode-subtitle\": \"Có tự động phát tập tiếp theo khi kết thúc tập hiện tại.\",\n  \"auto-playback\": \"Tự động phát lại\",\n  \"auto-playback-subtitle\": \"Có sử dụng chức năng phát lại tự động.\",\n  \"auto-size\": \"Tự động thay đổi kích thước\",\n  \"auto-size-subtitle\": \"Có tự động điều chỉnh kích thước của trình phát để phù hợp với video.\",\n  \"auto-skip-op-ed\": \"Tự động bỏ qua OP/ED\",\n  \"auto-skip-op-ed-subtitle\": \"Có tự động bỏ qua OP/ED khi chơi.\",\n  \"auto-switch-subtitle\": \"Tự động chuyển phụ đề\",\n  \"auto-switch-subtitle-subtitle\": \"Tự động chuyển phụ đề sau khi thêm phụ đề vào danh sách phụ đề.\",\n  \"autoplay\": \"Tự động phát\",\n  \"autoplay-subtitle\": \"Có chơi tự động không.\",\n  \"autumn\": \"Autumn\",\n  \"blue\": \"Xanh dương\",\n  \"bumblebee\": \"Bumblebee\",\n  \"business\": \"Business\",\n  \"card\": \"Card\",\n  \"cmyk\": \"Cmyk\",\n  \"coffee\": \"Coffee\",\n  \"corporate\": \"Corporate\",\n  \"cupcake\": \"Cupcake\",\n  \"custom-theme-color\": \"Màu chủ đề tùy chỉnh\",\n  \"cyan\": \"Xanh lam\",\n  \"cyberpunk\": \"Cyberpunk\",\n  \"dark-mode\": \"Chế độ tối\",\n  \"defaults\": \"Mặc định\",\n  \"defaults-subtitle\": \"Cài đặt mặc định cho trình phát. Bạn có thể cần tải lại trình phát để áp dụng các thay đổi.\",\n  \"detail\": \"Detail\",\n  \"dracula\": \"Dracula\",\n  \"emerald\": \"Emerald\",\n  \"en\": \"Tiếng Anh\",\n  \"experiments\": \"Thử nghiệm\",\n  \"experiments-subtitle\": \"Đây là một tập hợp các thử nghiệm mà chúng tôi đang làm việc để có thể sử dụng được.\",\n  \"fantasy\": \"Fantasy\",\n  \"fast-forward\": \"Tua nhanh\",\n  \"fast-forward-10s\": \"Tua nhanh 10 giây\",\n  \"fast-forward-5s\": \"Tua nhanh 5 giây\",\n  \"fast-forward-subtitle\": \"Nhấn và giữ video để tua nhanh trên di động\",\n  \"fast-rewind-10s\": \"Tua lại 10 giây\",\n  \"fast-rewind-5s\": \"Tua lại 5 giây\",\n  \"forest\": \"Forest\",\n  \"fr\": \"Tiếng Pháp\",\n  \"garden\": \"Garden\",\n  \"general\": \"Tổng quan\",\n  \"gestures\": \"Cử chỉ\",\n  \"gestures-subtitle\": \"Cài đặt cử chỉ của trình phát\",\n  \"green\": \"Xanh lá cây\",\n  \"halloween\": \"Halloween\",\n  \"header\": \"Header\",\n  \"header-subtitle\": \"Cài đặt header\",\n  \"infinite-scroll\": \"Infinite scroll\",\n  \"keyboard\": \"Phím tắt bàn phím\",\n  \"keyboard-subtitle\": \"Các phím tắt bàn phím cho trình phát.\",\n  \"language\": \"Ngôn ngữ\",\n  \"layout\": \"Bố cục\",\n  \"layout-subtitle\": \"Tùy chỉnh bố cục và kiểu dáng ứng dụng.\",\n  \"lemonade\": \"Lemonade\",\n  \"light-dark-only\": \"Chỉ sáng & tối\",\n  \"list-loading-type\": \"List loading type\",\n  \"list-view-type\": \"List view type\",\n  \"lofi\": \"Lofi\",\n  \"loop\": \"Lặp lại\",\n  \"loop-subtitle\": \"Có lặp lại khi trình phát kết thúc.\",\n  \"luxury\": \"Luxury\",\n  \"media-list-banner\": \"Media list banner\",\n  \"media-list-banner-subtitle\": \"Media list banner settings customization\",\n  \"media-list-grid\": \"Media list grid\",\n  \"media-list-grid-subtitle\": \"Media list grid settings customization\",\n  \"mini-progressbar\": \"Thanh tiến trình nhỏ\",\n  \"mini-progressbar-subtitle\": \"Mini Progress Bar, chỉ xuất hiện khi trình phát mất tập trung và đang chơi.\",\n  \"mute-trailer\": \"Tắt tiếng trailer\",\n  \"mute-unmute\": \"Tắt tiếng/ Bật tiếng\",\n  \"muted\": \"Tắt tiếng\",\n  \"muted-subtitle\": \"Có bật tiếng khi trình phát bắt đầu.\",\n  \"night\": \"Night\",\n  \"or\": \"hoặc\",\n  \"pagination\": \"Pagination\",\n  \"pastel\": \"Pastel\",\n  \"pic-in-pic\": \"Hình trong hình\",\n  \"pic-in-pic-subtitle\": \"Có hiển thị nút chuyển đổi hình ảnh trong hình ảnh trong thanh điều khiển dưới cùng.\",\n  \"pink\": \"Hồng\",\n  \"play-trailer\": \"Phát trailer\",\n  \"player\": \"Trình phát\",\n  \"player-features\": \"Tính năng trình phát\",\n  \"player-features-subtitle\": \"Các tính năng của trình phát. Bạn có thể cần tải lại trình phát để áp dụng các thay đổi.\",\n  \"purple\": \"Tím\",\n  \"red\": \"Đỏ\",\n  \"retro\": \"Retro\",\n  \"screenshot\": \"Chụp màn hình\",\n  \"screenshot-subtitle\": \"Có hiển thị nút chụp màn hình trong thanh điều khiển dưới cùng hay không.\",\n  \"seek-to-end\": \"Tua về cuối video.\",\n  \"seek-to-percent\": \"Tua đến từ 10% đến 90% phần trăm của video.\",\n  \"seek-to-start\": \"Tua về đầu video.\",\n  \"settings\": \"Cài đặt\",\n  \"show-breadcrumb\": \"Hiển thị breadcrumb\",\n  \"show-logo\": \"Hiển thị logo nếu có\",\n  \"show-skip-op-ed-button\": \"Hiển thị nút bỏ qua OP/ED\",\n  \"show-skip-op-ed-button-subtitle\": \"Hiển thị nút bỏ qua OP/ED trong thanh điều khiển dưới cùng.\",\n  \"show-spotlight\": \"Hiển thị spotlight\",\n  \"show-subtitle\": \"Hiển thị phụ đề\",\n  \"show-subtitle-subtitle\": \"Mặc định hiển thị phụ đề trong trình phát.\",\n  \"show-top-pagination\": \"Hiển thị phân trang trên cùng\",\n  \"sidebar\": \"Thanh bên\",\n  \"sidebar-boxed-mode\": \"Boxed\",\n  \"sidebar-hover-mode\": \"Hover\",\n  \"sidebar-mini-mode\": \"Mini\",\n  \"sidebar-mode\": \"Chế độ thanh bên\",\n  \"sidebar-sheet-mode\": \"Sheet\",\n  \"sidebar-subtitle\": \"Cài đặt thanh bên\",\n  \"subtitle-background-color\": \"Màu nền phụ đề\",\n  \"subtitle-background-opacity\": \"Độ mờ nền phụ đề\",\n  \"subtitle-font-color\": \"Màu chữ phụ đề\",\n  \"subtitle-font-size\": \"Kích thước chữ phụ đề\",\n  \"subtitle-text-effects\": \"Hiệu ứng chữ phụ đề\",\n  \"subtitle-window-color\": \"Màu cửa sổ phụ đề\",\n  \"subtitle-window-opacity\": \"Độ mờ cửa sổ phụ đề\",\n  \"subtitles\": \"Phụ đề\",\n  \"subtitles-subtitle\": \"Phụ đề Cài đặt cho trình phát.\",\n  \"swipe-to-fullscreen\": \"Vuốt để chuyển sang toàn màn hình\",\n  \"swipe-to-fullscreen-subtitle\": \"Có cho phép vuốt để chuyển sang toàn màn hình.\",\n  \"swipe-to-seek\": \"Vuốt để tua\",\n  \"swipe-to-seek-subtitle\": \"Có cho phép vuốt để tua.\",\n  \"synthwave\": \"Synthwave\",\n  \"table\": \"Table\",\n  \"theme\": \"Giao diện\",\n  \"theme-color\": \"Màu chủ đề\",\n  \"theme-subtitle\": \"Tùy chỉnh giao diện cho ứng dụng.\",\n  \"toggle-fullscreen\": \"Chuyển đổi toàn màn hình. Nếu đang ở sẵn toàn màn hình, nhấn lại F hoặc escape để thoát\",\n  \"toggle-play-pause\": \"Chơi/tạm dừng\",\n  \"toggle-subtitle\": \"Hiển thị phụ đề nếu có. Để ẩn phụ đề, nhấn lại C.\",\n  \"valentine\": \"Valentine\",\n  \"vi\": \"Tiếng Việt\",\n  \"volume-down\": \"Giảm âm lượng\",\n  \"volume-up\": \"Tăng âm lượng\",\n  \"winter\": \"Winter\",\n  \"wireframe\": \"Wireframe\",\n  \"yellow\": \"Vàng\"\n}\n"
  },
  {
    "path": "public/locales/vi/sidebar.json",
    "content": "{\n  \"anime\": \"Anime\",\n  \"anime-discover\": \"Khám phá\",\n  \"anime-footer\": \"Anime\",\n  \"anime-popular\": \"Phổ biến\",\n  \"anime-popular-subtitle\": \"Anime được nhiều người xem\",\n  \"anime-random\": \"Ngẫu nhiên\",\n  \"anime-random-subtitle\": \"Anime được chọn ngẫu nhiên từ danh sách\",\n  \"anime-recent-episodes\": \"Tập mới nhất\",\n  \"anime-recent-episodes-subtitle\": \"Tập mới nhất của bộ anime đang phát sóng\",\n  \"anime-trending\": \"Xu hướng\",\n  \"anime-trending-subtitle\": \"Amine đang nhận được sự yêu thích và chú ý của người xem\",\n  \"collections\": \"Bộ sưu tập\",\n  \"discover\": \"Khám phá\",\n  \"history\": \"Lịch sử\",\n  \"home\": \"Trang chủ\",\n  \"movies\": \"Phim lẻ\",\n  \"movies-discover\": \"Khám phá\",\n  \"movies-footer\": \"Phim lẻ\",\n  \"movies-now-playing\": \"Đang chiếu\",\n  \"movies-now-playing-subtitle\": \"Phim lẻ đang được chiếu tại rạp\",\n  \"movies-popular\": \"Phổ biến\",\n  \"movies-popular-subtitle\": \"Phim lẻ được nhiều người xem\",\n  \"movies-top-rated\": \"Đánh giá cao\",\n  \"movies-top-rated-subtitle\": \"Phim được xếp hạng cao, dựa trên xếp hạng của người xem và đánh giá của giới phê bình\",\n  \"movies-upcoming\": \"Sắp chiếu\",\n  \"movies-upcoming-subtitle\": \"Phim lẻ sắp được chiếu tại rạp\",\n  \"people\": \"Người nổi tiếng\",\n  \"search\": \"Tìm kiếm\",\n  \"search-anime\": \"Tìm kiếm Anime\",\n  \"search-movies\": \"Tìm kiếm Phim lẻ\",\n  \"search-people\": \"Tìm kiếm Diễn viên\",\n  \"search-tv-shows\": \"Tìm kiếm Phim bộ\",\n  \"settings\": \"Cài đặt\",\n  \"trending\": \"Xu hướng\",\n  \"tv-shows\": \"Phim bộ\",\n  \"tv-shows-airing-today\": \"Phát sóng hôm nay\",\n  \"tv-shows-airing-today-subtitle\": \"Phim bộ đang phát trên truyền hình hoặc dịch vụ phát trực tuyến\",\n  \"tv-shows-discover\": \"Khám phá\",\n  \"tv-shows-footer\": \"Phim bộ\",\n  \"tv-shows-on-the-air\": \"Đang phát trên TV\",\n  \"tv-shows-on-the-air-subtitle\": \"Phim bộ hiện có sẵn để xem\",\n  \"tv-shows-popular\": \"Phổ biến\",\n  \"tv-shows-popular-subtitle\": \"Phim bộ được nhiều người xem\",\n  \"tv-shows-top-rated\": \"Đánh giá cao\",\n  \"tv-shows-top-rated-subtitle\": \"Phim bộ được xếp hạng cao, dựa trên xếp hạng của người xem và đánh giá của nhà phê bình\",\n  \"featured-lists\": \"Danh sách nổi bật\"\n}\n"
  },
  {
    "path": "remix.config.js",
    "content": "const { flatRoutes } = require('remix-flat-routes')\n\n/**\n * @type {import('@remix-pwa/dev').WorkerConfig}\n */\nmodule.exports = {\n  //\n  // Remix Settings\n  //\n\t// appDirectory: \"app\",\n  // assetsBuildDirectory: \"public/build\",\n  browserNodeBuiltinsPolyfill: { modules: { url: true } },\n\t// ignore all files in routes folder to prevent\n  // default remix convention from picking up routes\n  ignoredRouteFiles: ['**/.*'],\n  // publicPath: \"/build/\",\n\tpostcss: true,\n  routes: async defineRoutes => {\n    return flatRoutes('routes', defineRoutes)\n  },\n  // serverBuildPath: \"build/index.js\",\n  serverDependenciesToBundle: [\n    /^swiper.*/,\n    /^ssr-window.*/,\n    /^dom7.*/,\n    /^react-photoswipe-gallery.*/,\n    /^photoswipe.*/,\n    /^remix-utils.*/,\n    /^is-ip.*/,\n    /^ip-regex.*/,\n    /^super-regex.*/,\n    /^clone-regex.*/,\n    /^function-timeout.*/,\n    /^time-span.*/,\n    /^convert-hrtime.*/,\n    /^is-regexp.*/,\n    /@remix-pwa\\/.*/,\n    /@font-source\\/.*/\n  ],\n  serverModuleFormat: 'cjs',\n  tailwind: true,\n  //\n  // Remix PWA Settings\n  //\n  // entryWorkerFile: '<appDir>/entry.worker.ts',\n  // worker: '@remix-pwa/runtime',\n  workerName: 'sw',\n  workerMinify: true,\n  // workerBuildDirectory: './build',\n  // workerSourcemap: false,\n};\n"
  },
  {
    "path": "remix.env.d.ts",
    "content": "/// <reference types=\"@remix-run/dev\" />\n/// <reference types=\"@remix-run/node/globals\" />\n"
  },
  {
    "path": "tailwind.config.js",
    "content": "const { withTV } = require('tailwind-variants/transformer');\nconst plugin = require('tailwindcss/plugin');\nconst { nextui } = require('@nextui-org/theme');\nconst themesConfig = require('./app/styles/themes.config');\nconst defaultTheme = require('tailwindcss/defaultTheme');\n\n/** @type {import('tailwindcss').Config} */\nmodule.exports = withTV({\n  content: [\n    './app/**/*.{js,ts,jsx,tsx}',\n    './node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}',\n  ],\n  theme: {\n    extend: {\n      colors: {\n        'player-subtitle-color': 'var(--art-subtitle-color)',\n        'player-subtitle-window-color': 'var(--art-subtitle-window-color)',\n        'player-subtitle-background-color': 'var(--art-subtitle-background-color)',\n        'movie-brand-color': 'var(--theme-movie-brand)',\n      },\n      keyframes: {\n        shadowAnimation: {\n          '0%, 100%': {\n            transform: 'scale(1, 1)',\n          },\n          '50%': {\n            transform: 'scale(1.2, 1)',\n          },\n        },\n        jumpAnimation: {\n          '15%': {\n            borderBottomRightRadius: '3px',\n          },\n          '25%': {\n            transform: 'translateY(9px) rotate(22.5deg)',\n          },\n          '50%': {\n            transform: 'translateY(18px) scale(1, .9) rotate(45deg)',\n            borderBottomRightRadius: '40px',\n          },\n          '75%': {\n            transform: 'translateY(9px) rotate(67.5deg)',\n          },\n          '100%': {\n            transform: 'translateY(0) rotate(90deg)',\n          },\n        },\n        progressBarStripes: {\n          '0%': { backgroundPosition: '40px 0' },\n          '100%': { backgroundPosition: '0 0' },\n        },\n      },\n      animation: {\n        shadow: 'shadowAnimation 500ms linear infinite',\n        jump: 'jumpAnimation 500ms linear infinite',\n        progressBarStripes: 'progressBarStripes 2s linear infinite',\n      },\n      gridTemplateAreas: {\n        wide: ['image title', 'image info', 'image buttons'],\n        small: ['image title', 'info info', 'buttons buttons'],\n      },\n      fontFamily: {\n        sans: ['Sora', ...defaultTheme.fontFamily.sans],\n        system: 'system-ui',\n      },\n      fontSize: {\n        xs: ['0.75rem', { lineHeight: '1rem', letterSpacing: '-0.05em' }],\n        sm: ['0.875rem', { lineHeight: '1.25rem', letterSpacing: '-0.05em' }],\n        base: ['1rem', { lineHeight: '1.5rem', letterSpacing: '-0.025em' }],\n        lg: ['1.125rem', { lineHeight: '1.75rem', letterSpacing: '0' }],\n        xl: ['1.25rem', { lineHeight: '1.5', letterSpacing: '0' }],\n        '2xl': ['1.5rem', { lineHeight: '1.5', letterSpacing: '0' }],\n        '3xl': ['1.875rem', { lineHeight: '1.5', letterSpacing: '0' }],\n        '4xl': ['2.25rem', { lineHeight: '1.5', letterSpacing: '0' }],\n        'player-subtitle-font-size': 'var(--art-subtitle-custom-font-size)',\n      },\n      textShadow: {\n        none: 'none',\n        sm: '0 1px 2px var(--tw-shadow-color)',\n        DEFAULT: '0 2px 4px var(--tw-shadow-color)',\n        lg: '0 8px 16px var(--tw-shadow-color)',\n        player: 'var(--art-subtitle-text-shadow)',\n      },\n      width: {\n        logo: 'calc(var(--movie-logo-width) * 1px)',\n        'logo-sm': 'calc(var(--movie-logo-width-sm) * 1px)',\n      },\n    },\n    screens: {\n      '2xs': '375px',\n      xs: '425px',\n      sm: '650px',\n      md: '768px',\n      lg: '1024px',\n      xl: '1280px',\n      '2xl': '1400px',\n      '3xl': '1600px',\n      '4xl': '1920px',\n      '5xl': '2560px',\n      'nextui-xs': '650px',\n      'nextui-sm': '960px',\n      'nextui-md': '1280px',\n      'nextui-lg': '1400px',\n      'nextui-xl': '1920px',\n    },\n  },\n  darkMode: 'class',\n  plugins: [\n    plugin(function ({ matchUtilities, theme }) {\n      matchUtilities(\n        {\n          'text-shadow': (value) => ({\n            textShadow: value,\n          }),\n        },\n        { values: theme('textShadow') },\n      );\n    }),\n    require('tailwind-scrollbar-hide'),\n    require('@savvywombat/tailwindcss-grid-areas'),\n    require('prettier-plugin-tailwindcss'),\n    require('tailwindcss-animate'),\n    nextui({\n      prefix: 'theme',\n      themes: themesConfig,\n    }),\n  ],\n  variants: {\n    gridTemplateAreas: ['responsive'],\n  },\n});\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n\t\"include\": [\"remix.env.d.ts\", \"**/*.ts\", \"**/*.tsx\"],\n\t\"compilerOptions\": {\n\t\t\"esModuleInterop\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n    \"skipLibCheck\": true,\n\t\t\"target\": \"ES2022\",\n\t\t\"allowJs\": true,\n\t\t\"resolveJsonModule\": true,\n\t\t\"moduleDetection\": \"force\",\n\t\t\"strict\": true,\n\t\t\"lib\": [\"DOM\", \"DOM.Iterable\", \"ES2022\"],\n\t\t\"isolatedModules\": true,\n\t\t\"noImplicitAny\": true,\n\t\t\"jsx\": \"react-jsx\",\n\t\t\"moduleResolution\": \"Bundler\",\n\t\t\"module\": \"ES2022\",\n\t\t\"noEmit\": true,\n\t\t\"baseUrl\": \".\",\n\t\t\"paths\": {\n\t\t\t\"~/*\": [\"./app/*\"]\n\t\t},\n\t}\n}"
  },
  {
    "path": "vercel.json",
    "content": "{\n  \"build\": {\n    \"env\": {\n      \"ENABLE_FILE_SYSTEM_API\": \"1\"\n    }\n  }\n}\n"
  }
]