Compare commits

...
Author SHA1 Message Date
nicoagostini 343a4f8275 fix/depreciation again 2025-05-22 13:09:26 -07:00
nicoagostini 0129315523 fix/depreciation again 2025-05-22 13:02:04 -07:00
SowinskiBraeden d82bfd5b56 fix/name allow spaces 2025-05-22 12:23:34 -07:00
Joaquin Paredes a7bb955c8a Merge pull request #39 from JoaquinPar/dev
Merge dev into main
2025-05-22 11:14:30 -07:00
SowinskiBraeden 19081e3bd3 merge latest dev 2025-05-22 11:13:00 -07:00
SowinskiBraeden 7269814501 fix/limit age 2025-05-22 11:12:36 -07:00
nicoagostini 16f267bcd4 fix/calculations capped if you try to retire today 2025-05-22 11:09:15 -07:00
nicoagostini 8998283552 Merge branch 'dev' of https://github.com/JoaquinPar/2800-202510-BBY14 into dev 2025-05-22 10:43:29 -07:00
nicoagostini 5b6ce3b09d fix/Depreciation fixed, if asset value < 1000, use the input value 2025-05-22 10:42:53 -07:00
SowinskiBraeden e45b7ca946 merge latest dev 2025-05-22 10:38:50 -07:00
SowinskiBraeden c0e2d01ea6 fix/asset name aphanum restriction 2025-05-22 10:38:22 -07:00
nicoagostini 3d55a2b0d0 fix/ vehicle depreciation capped at one thousand 2025-05-22 10:37:05 -07:00
nicoagostini ec309fbafd fix/ Reinforce user to complete the financial questionnaire 2025-05-22 10:33:13 -07:00
knighthawk4227 c1adb31657 fixing indigo button 2025-05-22 00:21:35 -07:00
knighthawk4227 05fa5558e3 open modal of plan after pressing create new plan 2025-05-21 17:02:35 -07:00
knighthawk4227 d2e69f230f fixing conflicts 2025-05-21 16:54:17 -07:00
Nícolas Agostini 0672981e69 Merge pull request #38 from JoaquinPar/feature/vehicle-depreciation
feature/vehicle depreciation calculated towards total assets value
2025-05-21 16:40:15 -07:00
nicoagostini 5b147d4f46 feature/vehicle depreciation calculated towards total assets value 2025-05-21 16:39:19 -07:00
SowinskiBraeden d6425043cf Merge branch 'dev' of github.com:JoaquinPar/2800-202510-BBY14 into dev 2025-05-21 16:38:01 -07:00
SowinskiBraeden cf3d3738f1 fix/default hide year on asset create 2025-05-21 16:37:48 -07:00
knighthawk4227 cc6fd937db buttons blue 2025-05-21 16:16:29 -07:00
SowinskiBraeden b12d4bebe1 update/add year to car assets 2025-05-21 15:40:29 -07:00
SowinskiBraeden 7c09df0924 fix/asset modified date + des whitespace 2025-05-21 15:13:09 -07:00
SowinskiBraeden 44e674c7c9 Merge branch 'dev' of github.com:JoaquinPar/2800-202510-BBY14 into dev 2025-05-21 15:03:45 -07:00
SowinskiBraeden 7c9e92345d fix/assets empty description 2025-05-21 15:03:26 -07:00
nicoagostini 17e3bdf4f4 fix/margin top on assets.ejs changed to match the application 2025-05-21 14:40:08 -07:00
SowinskiBraeden da0bfea655 Merge branch 'dev' of github.com:JoaquinPar/2800-202510-BBY14 into dev 2025-05-21 13:59:20 -07:00
SowinskiBraeden 7fe94e1934 fix/password 2025-05-21 13:58:39 -07:00
nicoagostini 7fd7fa28ff fix/Router after editing fixed to plan/PlanId 2025-05-21 13:56:56 -07:00
SowinskiBraeden 163cb73b9d fix/validation error messages 2025-05-21 12:06:57 -07:00
SowinskiBraeden 9a61d362be update/project structure 2025-05-21 11:38:16 -07:00
SowinskiBraeden db392d1878 Merge branch 'dev' of github.com:JoaquinPar/2800-202510-BBY14 into dev 2025-05-21 11:33:43 -07:00
SowinskiBraeden e50047b26a refactor/add headers to client side scripts 2025-05-21 11:33:26 -07:00
Joaquin 5bc26e334e fix/fixed input fields on login/signup not being required 2025-05-21 11:22:36 -07:00
SowinskiBraeden ce9345fad9 refactor/add required indicators 2025-05-21 11:17:12 -07:00
SowinskiBraeden 90d507e33c refactor/format assets 2025-05-21 11:14:47 -07:00
SowinskiBraeden dd9a0a1027 fix/allow decimal values in assets 2025-05-21 11:04:42 -07:00
Joaquin 231aca9ec5 fix/added required tooltips to some field forms 2025-05-21 11:04:08 -07:00
SowinskiBraeden 796dc245b2 fix/password reset email body 2025-05-21 10:53:34 -07:00
SowinskiBraeden 3dc1faaed5 fix/password reset 2025-05-21 10:47:27 -07:00
Joaquin 5b3d5cdce1 fix/improved user info retention on signup/login forms 2025-05-21 09:39:13 -07:00
nicoagostini 07b8325a14 fix/required fields are marked 2025-05-20 20:12:28 -07:00
nicoagostini c0174c2b22 fix/ now assets display the location currency OR CAD as default 2025-05-20 16:45:49 -07:00
nicoagostini 08397a1c89 refactor/edit plan 'reset' button added 2025-05-20 14:56:03 -07:00
Nícolas Agostini 273009f91f Merge pull request #37 from JoaquinPar/refactor/plans
refactor/ new plan as a modal pop up. Deletion and edition on plan cr…
2025-05-20 14:27:20 -07:00
knighthawk4227 436e242cf1 message if no plans 2025-05-20 14:25:48 -07:00
nicoagostini f1afcbd785 refactor/ new plan as a modal pop up. Deletion and edition on plan created 2025-05-20 14:25:27 -07:00
Joaquin dd1ad02b32 Merge branch 'dev' of https://github.com/JoaquinPar/2800-202510-BBY14 into dev 2025-05-20 14:07:09 -07:00
Joaquin 50bd21b164 fix/user info is now kept on form reset 2025-05-20 14:05:34 -07:00
knighthawk4227 ef858bcf00 no plan form/ check 2025-05-20 12:57:17 -07:00
Joaquin de99301738 fix/fixed background of password reset 2025-05-20 11:16:55 -07:00
Joaquin Paredes 7c34094a93 Merge pull request #36 from JoaquinPar/dev
Sprint 3 completed
2025-05-20 09:02:27 -07:00
Joaquin e320459955 fix/fixed all color related problems 2025-05-20 08:56:44 -07:00
knighthawk4227 8c0e07abb3 dashboard col issue 2025-05-16 23:31:28 -07:00
Nícolas Agostini edb0021cc6 Merge pull request #35 from JoaquinPar/feature/months-left
feature/ calculations done, being displayed dynamically in the plan card
2025-05-16 19:37:22 -07:00
nicoagostini 69624f859c feature/ calculations done, being displayed dynamically in the plan card 2025-05-16 19:36:17 -07:00
nicoagostini 53e72e2a19 fix/ dashboard progression bar not updating when assets changed 2025-05-16 16:19:35 -07:00
nicoagostini 043c24a417 fix/progress bar limitation added 2025-05-16 12:46:23 -07:00
Joaquin 629e4f8ae8 fix/fixed planDetail page padding 2025-05-16 12:28:37 -07:00
Nícolas Agostini e2df35929f Merge pull request #33 from JoaquinPar/fix/planDetail-padding
Fix/plan detail padding
2025-05-16 12:26:35 -07:00
Nícolas Agostini b1743d91c4 Merge branch 'dev' into fix/planDetail-padding 2025-05-16 12:26:28 -07:00
nicoagostini 5d67f4f5ed fix/planDetail padding fixed to avoid overlap 2025-05-16 12:25:29 -07:00
nicoagostini 505f0484bc fix/planDetail.ejs padding adjusted to fix overlap 2025-05-16 12:14:42 -07:00
Joaquin cc63b83288 fix/fixed spacing in planDetail page 2025-05-16 12:09:46 -07:00
knighthawk4227 0eaa82b121 padding for mobile 2025-05-16 12:02:06 -07:00
Joaquin 03b0dd8fa6 fix/fixed navbar pop-up element shadow 2025-05-16 11:57:53 -07:00
Joaquin 6faa951636 fix/fixed navbar pop-up element shadow 2025-05-16 11:56:06 -07:00
Nícolas Agostini 58a6e4724f Merge pull request #32 from JoaquinPar/refactor/ai-challenge2
refactor/ai-challenge2 removed from pages where the user is not logge…
2025-05-16 11:48:48 -07:00
nicoagostini f5f1516a76 refactor/ai-challenge2 removed from pages where the user is not logged in 2025-05-16 11:47:40 -07:00
knighthawk4227 692f195e27 google chrome browser fix 2025-05-16 11:25:08 -07:00
knighthawk4227 5ec2de0631 dashboard changed for mobile 2025-05-16 10:50:47 -07:00
Joaquin 59f8677e7c solved merge conflicts 2025-05-16 10:48:19 -07:00
Joaquin 480ea236ec fix/small fixes to page layouts 2025-05-16 00:41:17 -07:00
Joaquin 6ecd1ee848 implemented more page 2025-05-16 00:22:13 -07:00
Joaquin c839d023a7 fix/removed scrolling from login/signup pages 2025-05-15 21:46:58 -07:00
Joaquin cfcfd7778d feature/more page in nav bar completed 2025-05-15 21:30:58 -07:00
SowinskiBraeden 450e46f9e5 Merge pull request #30 from JoaquinPar/update/password_security
update/password security
2025-05-15 13:38:17 -07:00
SowinskiBraeden 3940089fa6 update/password security 2025-05-15 08:20:28 -07:00
knighthawk4227 0834723c99 Merge branch 'dev' into feature/dashboardInfo 2025-05-14 21:51:26 -07:00
knighthawk4227 1acb749f1f name of js file 2025-05-14 21:50:41 -07:00
knighthawk4227 563fa502d1 progress bar on dashboard & number formatting 2025-05-14 21:47:50 -07:00
SowinskiBraeden aa9e412fa1 Merge pull request #29 from JoaquinPar/refactor/assets
Refactor/assets
2025-05-14 15:06:00 -07:00
SowinskiBraeden 60238b7a9a add dropdown scripts 2025-05-14 15:05:14 -07:00
SowinskiBraeden 61db8e162f Merge dev into refactor/assets 2025-05-14 14:46:34 -07:00
SowinskiBraeden 1c5031f48c split assets into partials 2025-05-14 14:43:34 -07:00
SowinskiBraeden b040f12eaf split assets into partials 2025-05-14 14:43:17 -07:00
nicoagostini 4a02b75a4d fix/Footer scripts moved to a dedicated file 2025-05-14 12:53:58 -07:00
Joaquin f7377f6844 feature/most functionality created 2025-05-14 12:42:50 -07:00
SowinskiBraeden 34c6dd2c66 Merge pull request #28 from JoaquinPar/fix/account_details_update
Fix/account details update
2025-05-14 12:16:34 -07:00
SowinskiBraeden 1f3109a68f Merge branch 'dev' of github.com:JoaquinPar/2800-202510-BBY14 into fix/account_details_update 2025-05-14 12:13:04 -07:00
SowinskiBraeden 9ae23ce25e fix account details not updating + update personal info 2025-05-14 12:12:48 -07:00
Nícolas Agostini c41f38ce6b Merge pull request #27 from JoaquinPar/feature/ai-challenge2
Feature/ai challenge2
2025-05-14 11:24:40 -07:00
Nícolas Agostini 5771f1372f Merge branch 'dev' into feature/ai-challenge2 2025-05-14 11:24:31 -07:00
nicoagostini 73d61d76de feature/AI feature updated to accept user input 2025-05-14 11:22:28 -07:00
nicoagostini 50e2d95fda feature/AI feature updated to accept user input 2025-05-14 11:21:10 -07:00
Joaquin 370d5d3292 merge dev into feature/challenge1_error_message 2025-05-14 09:41:39 -07:00
Joaquin c12bbe8336 removed debugging code 2025-05-14 09:40:06 -07:00
Joaquin 0d59c60a28 merge dev into feature/challenge1_error_message 2025-05-14 09:39:12 -07:00
Joaquin 5e2ac94212 fully implemented error handling for challenge 1 2025-05-14 09:37:09 -07:00
SowinskiBraeden f5bc2de24a Merge pull request #26 from JoaquinPar/fix/session_issues
fix/login signup logout session bugs
2025-05-14 09:36:38 -07:00
SowinskiBraeden 3771288123 fix/login+signup+logout session bugs 2025-05-14 09:34:32 -07:00
Nícolas Agostini ccae6a51ff Merge pull request #25 from JoaquinPar/feature/ai-challenge2
Feature/ai challenge2
2025-05-13 14:19:17 -07:00
nicoagostini 206b31e785 feature/ Surprise challenge #2 - AI pun button added and basic suggestion integrated in the plans 2025-05-13 14:18:21 -07:00
Joaquin 8166245ab1 feature/basic functionality added 2025-05-13 13:17:18 -07:00
Joaquin Paredes 79f0caa3c1 Merge pull request #23 from JoaquinPar/dev
End of Sprint 2
2025-05-10 16:35:08 -07:00
Nícolas Agostini 4f73959cfb Merge pull request #22 from JoaquinPar/fix/16-hovering-hides-progress
Progress bar background changed to black to avoid disappearing
2025-05-09 15:54:45 -07:00
nicoagostini 6444d7afa7 Progress bar background changed to black to avoid disappearing 2025-05-09 15:53:40 -07:00
Nícolas Agostini daa984a48f Merge pull request #21 from JoaquinPar/fix/questionnaireFix
Issue 19: Questionnaire more than once, fixed
2025-05-09 14:04:31 -07:00
nicoagostini 383bd15263 Issue 19: Questionnaire more than once, fixed 2025-05-09 14:03:35 -07:00
knighthawk4227 1e44e8ddfb moving resetLink post to forgotpass.js 2025-05-09 11:25:36 -07:00
knighthawk4227 cc875b4e22 Merge branch 'dev' of github.com:JoaquinPar/2800-202510-BBY14 into dev 2025-05-09 11:24:03 -07:00
knighthawk4227 f3dbbebb43 moving resetLink post to forgotpass.js 2025-05-09 11:18:25 -07:00
Joaquin 4ee885ac57 fix/reorganized scripts called in ejs files 2025-05-09 11:06:13 -07:00
knighthawk4227 f3b121fc71 Merge branch 'feature/forgotPass' into dev 2025-05-09 11:03:02 -07:00
knighthawk4227 07824e6355 merge cnflicts with dev 2025-05-09 11:02:31 -07:00
Joaquin 22850ab2a2 fix/removed redundant code and fixed UI challenge 1 problem 2025-05-09 10:55:02 -07:00
SowinskiBraeden 69fd776c0f fix/enhanced user input validation 2025-05-09 10:29:20 -07:00
knighthawk4227 671b67be9d Merge branch 'dev' into feature/forgotPass 2025-05-09 10:27:33 -07:00
knighthawk4227 99b36d3370 fixing conflict with dashboard.ejs 2025-05-09 10:26:18 -07:00
SowinskiBraeden 1f742d41c3 fix/signup using existing email 2025-05-09 10:23:34 -07:00
SowinskiBraeden 18bee21941 Merge branch 'dev' of github.com:JoaquinPar/2800-202510-BBY14 into dev 2025-05-09 10:07:16 -07:00
knighthawk4227 1fcd12c6da fixing merge conflicts 2025-05-09 10:04:24 -07:00
SowinskiBraeden 0a9c176274 fix/default asset icon creation 2025-05-09 10:00:51 -07:00
knighthawk4227 1587f363f7 fixing reset link session 2025-05-09 09:50:38 -07:00
SowinskiBraeden 540e191d27 refactor/remove debug statements 2025-05-09 09:49:14 -07:00
SowinskiBraeden 1e01e022bf Merge pull request #14 from JoaquinPar/feature/asset-select-icon
update+fix/asset icons + load geolocation scripts
2025-05-08 23:32:40 -07:00
SowinskiBraeden 73a510252b update+fix/asset icons + load geolocation scripts 2025-05-08 23:30:13 -07:00
SowinskiBraeden 42ea4ed8bc fix/issue #13 2025-05-08 21:40:39 -07:00
Nícolas Agostini 1ddb6a9900 Merge pull request #12 from JoaquinPar/feature/progressBar
Progress bar calculations added, displaying and updating the database
2025-05-08 20:10:37 -07:00
nicoagostini 9c0c560441 Progress bar calculations added, displaying and updating the database 2025-05-08 20:09:22 -07:00
SowinskiBraeden 5763f03288 fix/geolocation api key 2025-05-08 18:14:22 -07:00
SowinskiBraeden ed06b8731d fix/404 not found handling 2025-05-08 18:13:06 -07:00
SowinskiBraeden 345cbd2bf2 fix/database not awaited 2025-05-08 18:09:22 -07:00
SowinskiBraeden c4a90ceb15 fix/.env example + delete .vscode folder 2025-05-08 11:14:22 -07:00
SowinskiBraeden ac66323a78 fix/remove deprecated page 2025-05-08 11:02:10 -07:00
knighthawk4227 c64da1de46 fixing conflicts and merging dev 2025-05-08 10:44:38 -07:00
knighthawk4227 7a5d2e7490 fixing conflicts merging dev into this 2025-05-08 10:40:31 -07:00
Joaquin Paredes 32437a6e36 fix/fixed rendering pages 2025-05-08 10:38:32 -07:00
knighthawk4227 fc39bcf693 nodemailer dependencies 2025-05-08 10:32:10 -07:00
knighthawk4227 29d682c4e8 fixing joi validation 2025-05-07 19:26:00 -07:00
knighthawk4227 15dd4e0d20 forgot password validatation using joi 2025-05-07 19:09:25 -07:00
knighthawk4227 cb59e86d8a fixing merge conflicts and forgot password functionality 2025-05-07 17:44:06 -07:00
Joaquin cec777d1bf fix/fixed problem when routing to dashboard 2025-05-07 17:11:58 -07:00
Joaquin 605c7b3734 solved merge conflict 2025-05-07 17:02:09 -07:00
Joaquin 1b6c80f84d feature/fully implemented challenge 1 2025-05-07 16:56:26 -07:00
SowinskiBraeden ddf07eee11 fix/middleware+404 handling block plans 2025-05-07 16:24:09 -07:00
SowinskiBraeden 640ae859e5 Merge pull request #10 from JoaquinPar/feature/delete_account
Feature/delete account
2025-05-07 15:45:38 -07:00
SowinskiBraeden 54024bd7a2 fix/delete assets + plans on account delete 2025-05-07 15:33:07 -07:00
SowinskiBraeden 821843088d update/delete user account 2025-05-07 15:04:11 -07:00
Joaquin 2ad43139de merged dev and added some challenge 1 functionality 2025-05-07 13:06:22 -07:00
SowinskiBraeden 04a7fa428c Merge pull request #8 from JoaquinPar/feature/404
fix/middleware preventing 404 redirect
2025-05-07 13:04:54 -07:00
knighthawk4227 088f9ea55a reset pass logic 2025-05-07 13:03:26 -07:00
SowinskiBraeden cadbc2187d fix/middleware preventing 404 redirect 2025-05-07 13:01:24 -07:00
nicoagostini f417ac00fb fix/ Plans creation/display bug fixed 2025-05-07 11:37:33 -07:00
SowinskiBraeden b2e795427d fix/remove duplicated code 2025-05-07 10:53:28 -07:00
SowinskiBraeden 7ff74b8bbd fix/remove deprecated file 2025-05-07 10:51:54 -07:00
SowinskiBraeden 700ded35e4 Merge dev@latest into dev 2025-05-07 10:49:37 -07:00
knighthawk4227 617fc8d877 indentation of dashboard 2025-05-07 10:49:08 -07:00
SowinskiBraeden dbffe122a2 fix/reset forms on modal close 2025-05-07 10:43:05 -07:00
SowinskiBraeden d85a4120e2 fix/no assets message 2025-05-07 10:36:46 -07:00
SowinskiBraeden 3ad4e20588 update/render error messages 2025-05-07 10:35:09 -07:00
SowinskiBraeden aedfc17c36 update/render error messages 2025-05-07 10:34:06 -07:00
SowinskiBraeden 4c6945ecbc update/modify + delete assets 2025-05-07 10:33:02 -07:00
knighthawk4227 f0b1089e15 routes for forgotPass 2025-05-07 00:52:01 -07:00
knighthawk4227 a40b6d514b forgot password js 2025-05-06 23:33:05 -07:00
Joaquin 8507c4faaf solved merge conflict 2025-05-06 21:29:47 -07:00
Joaquin 17f11c85df feature/improved the getting of rates 2025-05-06 21:26:45 -07:00
Nícolas Agostini 0658a8a85c Merge pull request #6
Plan detail page finished with mock data
2025-05-06 18:42:34 -07:00
nicoagostini 173c0b63c4 Plan detail page finished with mock data 2025-05-06 18:36:25 -07:00
Joaquin 93a5c45dd9 Merge branch 'feature/challenge1' of https://github.com/JoaquinPar/2800-202510-BBY14 into feature/challenge1 2025-05-06 18:15:51 -07:00
Joaquin 1652d05fcd feature/push to pull 2025-05-06 18:15:32 -07:00
Joaquin Paredes 5bbfde3127 feature/improved data gathering from API 2025-05-06 18:13:31 -07:00
Joaquin a173b8c70c feature/app now receives exchange rates from API 2025-05-06 17:02:37 -07:00
SowinskiBraeden 78946bb6ca update/save asset to database 2025-05-06 16:59:49 -07:00
SowinskiBraeden 1f8579a052 update/save user from middleware to session 2025-05-06 16:59:33 -07:00
SowinskiBraeden 47fc1983f8 update/doc comments + create assets collection 2025-05-06 15:52:53 -07:00
nicoagostini d67718733d fix/ Route fixed for index/dashboard 2025-05-06 15:46:57 -07:00
SowinskiBraeden 803330b174 update/assets page and creation forms 2025-05-06 15:46:09 -07:00
knighthawk4227 e65af5336f Merge branch 'feature/dashboard' into dev 2025-05-06 15:09:16 -07:00
knighthawk4227 7b9b18f030 dashboard page with cards to fill in and add asset and new plan button 2025-05-06 15:08:04 -07:00
Joaquin f3a1a2191a merge feature/geolocation into dev 2025-05-06 14:48:52 -07:00
Joaquin e1626f6ae9 feature/completed getting geolocation data 2025-05-06 14:46:01 -07:00
Nícolas Agostini 05402432a6 Merge pull request #4 from JoaquinPar/feature/calculations
Feature/calculations
2025-05-06 14:32:54 -07:00
Nícolas Agostini 03c3f9a610 Merge branch 'dev' into feature/calculations 2025-05-06 14:32:43 -07:00
nicoagostini 9b998078b7 Questionnaire page cleaned up and updated 2025-05-06 14:30:43 -07:00
nicoagostini b944c37c02 Plans page created, listing the plans from the database, new plan page created and saving to database 2025-05-06 14:27:27 -07:00
Joaquin 83be006a83 feature/geolocation implemented 2025-05-06 13:38:00 -07:00
SowinskiBraeden 6d91934229 fix/ignore login err message on signup 2025-05-06 12:49:20 -07:00
SowinskiBraeden 97a1473e21 fix/require name on register 2025-05-06 12:46:21 -07:00
SowinskiBraeden 3bb9ede515 update/cancel changes 2025-05-06 11:52:53 -07:00
SowinskiBraeden b350cdad9f update/keep email input disabled 2025-05-06 11:46:14 -07:00
SowinskiBraeden f0d324151d update/error message style 2025-05-06 11:44:26 -07:00
SowinskiBraeden b88333e3b4 refactor/temp remove personal section 2025-05-06 11:41:14 -07:00
SowinskiBraeden 75bc3bee8f update/save modified account settings 2025-05-06 11:39:49 -07:00
SowinskiBraeden a2b4e8d36d refactor/remove debug statements 2025-05-06 11:19:10 -07:00
SowinskiBraeden 033401dbf7 update/profile page with user data 2025-05-06 11:15:18 -07:00
knighthawk4227 4bb9a01524 deleting projects table 2025-05-06 11:05:37 -07:00
knighthawk4227 47ab6c9eed merging dev with feature dashboard 2025-05-06 10:56:19 -07:00
knighthawk4227 b558a75f20 uncomment partials 2025-05-06 09:46:14 -07:00
SowinskiBraeden 1d8ec431b2 fix/tidy auth+user routers 2025-05-06 09:32:39 -07:00
Joaquin 4050e95cc3 fixed conflict 2025-05-06 09:30:30 -07:00
Joaquin ae3ddbeaeb Merge branch 'dev' of https://github.com/JoaquinPar/2800-202510-BBY14 into dev 2025-05-06 09:30:22 -07:00
Nícolas Agostini 84cd8b0bae Merge pull request #3 from JoaquinPar/feature/questions
feature/questionnaire page create + saving to MongoDb
2025-05-06 02:10:14 -07:00
Nícolas Agostini 5684e96a79 Merge branch 'dev' into feature/questions 2025-05-06 02:10:05 -07:00
nicoagostini 1e32ccfa17 feature/questionnaire page create + saving to MongoDb 2025-05-06 02:07:58 -07:00
knighthawk4227 5abec70c61 login and signup touch ups index page template added 2025-05-06 00:33:36 -07:00
Joaquin 54566f05b7 feature/fully implemented ejs inclusion to main files 2025-05-05 21:56:47 -07:00
Joaquin decdb299b7 Merge branch 'dev' of https://github.com/JoaquinPar/2800-202510-BBY14 into feature/layoutPage 2025-05-05 21:17:50 -07:00
Joaquin c8bf2e49ab feature/completed most of ejs implementation into other pages 2025-05-05 21:12:52 -07:00
nicoagostini f39b8555fa fix/session check fixed 2025-05-05 18:59:59 -07:00
nicoagostini fef7184404 fix/home.ejs include fix 2025-05-05 18:45:59 -07:00
SowinskiBraeden 6a8595d24b fix/impliment middleware 2025-05-05 18:39:43 -07:00
SowinskiBraeden 7c7098636a fix/middleware authentication + bad merge 2025-05-05 18:34:19 -07:00
Nícolas Agostini a446943d4a Merge pull request #2 from JoaquinPar/feature/questionnaire
Just a bug fix
2025-05-05 18:18:55 -07:00
Nícolas Agostini 612bb262d1 Merge branch 'dev' into feature/questionnaire 2025-05-05 18:18:45 -07:00
nicoagostini 6819b82723 Just a bug fix 2025-05-05 18:17:35 -07:00
SowinskiBraeden 5b1b36a553 fix/compare password on sign in 2025-05-05 18:12:19 -07:00
SowinskiBraeden 1114789e57 Merge latest dev into dev 2025-05-05 17:34:24 -07:00
SowinskiBraeden bb6e2eba2c fix/user routing 2025-05-05 17:33:27 -07:00
Joaquin f6640b23cf feature/some progress made on layouts 2025-05-05 17:07:51 -07:00
nicoagostini d0aa7a0fbd Landing moved to be the index page, routes added and landing page image route fixed 2025-05-05 16:46:16 -07:00
Joaquin 4be7ca3a8e Merge branch 'dev' into feature/layoutPage 2025-05-05 16:30:51 -07:00
Joaquin 10541d2428 merge feature/navBar with dev 2025-05-05 16:26:01 -07:00
Joaquin 7fa75457c9 feature/completed navbar/header functionality 2025-05-05 16:24:25 -07:00
Joaquin 8b6ff1354b resolved merge conflict 2025-05-05 14:27:24 -07:00
Joaquin 4bcf8dd812 feature/started work on layout.ejs 2025-05-05 14:21:30 -07:00
SowinskiBraeden fe7152b03b update/create middleware 2025-05-05 14:16:50 -07:00
SowinskiBraeden 87cad63425 update/return status codes 2025-05-05 13:17:30 -07:00
SowinskiBraeden d08fd30888 login/signup function with no sessions 2025-05-02 13:19:14 -07:00
SowinskiBraeden 7ef6282ac0 update login/signup.ejs pages 2025-05-02 13:18:51 -07:00
Joaquin Paredes 02a8374a58 feature/most nav bar functionality done 2025-05-02 13:15:38 -07:00
SowinskiBraeden 0e4d4d7daf fix project structure & static file 2025-05-02 11:41:13 -07:00
knighthawk4227 50dab82945 making login ejs and setting index blank for now 2025-05-02 02:07:55 -07:00
nicoagostini 8832768838 Merge branch 'feature/landingPage' into dev 2025-05-02 02:01:06 -07:00
knighthawk4227 44cee349e9 ejs login and signup with tailwind 2025-05-02 01:53:47 -07:00
Joaquin Paredes 5819b0d151 Merge pull request #1 from JoaquinPar/dev
Merging Dev into Main Sprint #0
2025-04-29 16:07:03 -07:00
106 changed files with 6335 additions and 229 deletions

No files matched your search

+7 -3
View File
@@ -1,4 +1,8 @@
mongoURI='mongodb://localhost:27017/' MONGO_URI='mongodb://localhost:27017/'
database='nameOfDatabase' DATABASE='nameOfDatabase'
PORT=8000 PORT=8000
SECRET='123456789'
GEOLOCATION_API='api_key'
EMAIL_USER=mail@example.com
EMAIL_PASS='password'
GOOGLE_API_KEY='api_key'
+6
View File
@@ -1,8 +1,14 @@
# Environment variables # Environment variables
.env .env
#vscode files
.vscode
# Node modules # Node modules
node_modules/ node_modules/
# Sync # Sync
*.sync-conflict* *.sync-conflict*
# VSCode
.vscode/
+14 -5
View File
@@ -53,13 +53,22 @@ Example:
``` ```
retirementCalculator/ retirementCalculator/
├── src/ ├── src/
├── views/ | ├── auth/
| ├── css/ | ├── database/
| ├── images/ ├── public/
| ── scripts/ │ │ ── images/
├── app.js │ │ ├── scripts/
│ │ └── svgs/
| |
│ ├── router/
│ ├── utils/
│ └── views/
│ └── partials/
├── .env.example ├── .env.example
├── .gitignore ├── .gitignore
├── app.js
├── CONTRIBUTING.md
├── LICENSE ├── LICENSE
├── package-lock.json ├── package-lock.json
├── package.json ├── package.json
-12
View File
@@ -1,12 +0,0 @@
<html>
<body>
Team Name: BBY-14
Team Members:
<ul>
<li> Joaquin Paredes </li>
<li> Nicolas Agostini </li>
<li> Mitchell Schaeffer </li>
<li> Braeden Sowinski </li>
</ul>
</body>
</html>
+129 -55
View File
@@ -1,94 +1,168 @@
const status = require("./src/util/statuses");
const MongoStore = require("connect-mongo");
const session = require("express-session");
const express = require('express'); const express = require('express');
const path = require('path'); const path = require('path');
const dotenv = require('dotenv'); const bcrypt = require('bcrypt');
const joi = require('joi');
dotenv.config(); require('dotenv').config();
const app = express(); const app = express();
const port = process.env.PORT || 3000; const port = process.env.PORT || 3000;
const mongoURI = process.env.MONGO_URI;
const database = process.env.DATABASE; // Database name
const secret = process.env.SECRET || "123-secret-xyz";
/*** Sessions ***/
app.use(session({
secret: secret,
store: MongoStore.create({ mongoUrl: `${mongoURI}${database}`, crypto: { secret: secret } }),
resave: true,
saveUninitialized: false,
cookie: { maxAge: 3600000 },
}));
app.set('view engine', 'ejs'); app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'src/views')); app.set('views', path.join(__dirname, 'src/views'));
app.use(express.urlencoded({ extended: true })); app.use(express.urlencoded({ extended: true }));
app.use("/static", express.static("./src/public"));
app.use("/images", express.static("./src/public/images"));
app.use(express.json());
/*** Database ***/
const { connectMongo, getCollection } = require("./src/database/connection");
/* let users;
STATIC ROUTINGS let assets;
*/ let plans;
async function initDatabase() {
const db = await connectMongo(mongoURI, database);
app.use("/scripts", express.static("./src/scripts")); // For any collection, init here
app.use("/css", express.static("./src/css")); users = await getCollection(db, "users");
app.use("/images", express.static("./src/images")); assets = await getCollection(db, "assets");
app.use("/views", express.static("./src/views")); plans = await getCollection(db, "plans");
}
/*** ROUTINGS ***/
/*
ROUTINGS
*/
app.get('/', (req, res) => { app.get('/', (req, res) => {
res.render('index'); if (!req.session.errMessage) req.session.errMessage = "";
});
app.get('/landing', (req, res) => {
res.render('landing'); res.render('landing');
return res.status(status.Ok);
}); });
app.get('/signup', (req, res) => { app.get('/signup', (req, res) => {
res.render('signup'); let error = req.session.errMessage;
delete req.session.errMessage;
const ignore = ["User not found", "Incorrect password"];
if (ignore.includes(error)) error = "";
if (req.query.name && req.query.email) {
res.render('signup', {
errMessage: error,
name: req.query.name,
email: req.query.email
});
} else {
res.render('signup', { errMessage: error });
}
return res.status(status.Ok);
}); });
app.get('/login', (req, res) => { app.get('/login', (req, res) => {
res.render('login'); if (req.session.authenticated) {
}); res.redirect("/home");
return res.status(status.Ok);
}
app.get('/home', (req, res) => { if (req.query.email) {
res.render('home'); res.render('login', {
errMessage: req.session.errMessage,
email: req.query.email
}); });
} else {
app.get('/assets', (req, res) => { res.render('login', { errMessage: req.session.errMessage });
res.render('assets'); }
}); return res.status(status.Ok);
app.get('/plans', (req, res) => {
res.render('plans');
});
app.get('/more', (req, res) => {
res.render('more');
});
app.get('/profile', (req, res) => {
res.render('profiles');
});
app.get('/settings', (req, res) => {
res.render('settings');
}); });
app.get('/aboutUs', (req, res) => { app.get('/aboutUs', (req, res) => {
res.render('aboutUs'); if (!req.session.authenticated) {
res.render('aboutUs', {
geoData: undefined
});
return res.status(status.Ok);
} else {
res.render('aboutUs', {
user: req.session.user,
geoData: req.session.geoData
});
return res.status(status.Ok);
}
}); });
app.get('/forgotPassword', (req, res) => {
app.post('/signup', (req, res) => { const error = req.session.error;
res.status(200); const reset = req.session.reset;
res.send('200 status code'); delete req.session.reset;
delete req.session.error;
res.render('forgotPass', { error: error, reset: reset });
return res.status(status.Ok);
}); });
app.post('/login', (req, res) => { // Reset with token given to user via email
res.status(200); app.get('/reset/:token', async (req, res) => {
res.send('200 status code'); const token = req.params.token;
const user = await users.findOne({
resetToken: token,
resetTokenExpires: { $gt: Date.now() },
}); });
if (!user) {
req.session.error = 'reset link not valid or has expired';
return res.redirect('/forgotPassword');
}
const error = req.session.error;
delete req.session.error;
res.render('resetPass', {
token: token,
errMessage: error,
});
});
// 404 handler - keep the actual notFound route please
// REALLY DONT DELETE THIS
app.get('/notFound', (req, res) => {
res.render('notFound');
return res.status(status.NotFound);
});
// Initialize database and start app
initDatabase().then(() => {
console.log("Successfully connected to MongoDB");
// Import authentication handler
app.use(require("./src/auth/authentication")(users));
app.use(require('./src/auth/forgotPass')(users));
// Import middleware & apply to user routes
const middleware = require("./src/auth/middleware")(users);
app.use(require('./src/router/user')(middleware, users, plans, assets));
// 404 handler
app.get('/*splat', (req, res) => { app.get('/*splat', (req, res) => {
res.status(404); res.render('notFound');
res.send('404 Not Found'); return res.status(status.NotFound);
}); });
// Start app
app.listen(port, () => { app.listen(port, () => {
console.log(`Server listening on port ${port}`); console.log(`Server listening on port ${port}`);
}); });
});
Generated Executable → Regular
+1417 -31
View File
File diff suppressed because it is too large. Load diff
+14 -4
View File
@@ -2,10 +2,10 @@
"name": "retirementcalculator", "name": "retirementcalculator",
"version": "0.1.0", "version": "0.1.0",
"description": "Retirement Calculator", "description": "Retirement Calculator",
"main": "./src/app.js", "main": "app.js",
"scripts": { "scripts": {
"start": "node ./src/app.js", "start": "node app.js",
"dev": "nodemon ./src/app.js" "dev": "nodemon app.js"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -18,9 +18,19 @@
}, },
"homepage": "https://github.com/JoaquinPar/2800-202510-BBY14#readme", "homepage": "https://github.com/JoaquinPar/2800-202510-BBY14#readme",
"dependencies": { "dependencies": {
"@google/genai": "^0.14.0",
"bcrypt": "^5.1.1",
"check-password-strength": "^3.0.0",
"connect-mongo": "^5.1.0",
"crypto": "^1.0.1",
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"ejs": "^3.1.10", "ejs": "^3.1.10",
"express": "^5.1.0" "express": "^5.1.0",
"express-session": "^1.18.1",
"joi": "^17.13.3",
"mongodb": "^6.16.0",
"nodemailer": "^7.0.3",
"sweetalert2": "^11.6.13"
}, },
"devDependencies": { "devDependencies": {
"nodemon": "^3.1.10" "nodemon": "^3.1.10"
+144
View File
@@ -0,0 +1,144 @@
const { passwordStrength } = require("check-password-strength");
const status = require("../util/statuses");
const bcrypt = require('bcrypt');
const joi = require("joi");
const salt = 12;
/**
* @param {MongoClient.collection} users db collection
* @returns {express.Router} authentication router
*/
module.exports = (users) => {
const router = require("express").Router();
router.get("/logout", (req, res) => {
req.session.destroy((err) => {
if (err) {
console.error("Failed to destroy session: ", err);
res.status(status.InternalServerError);
return res.redirect("back");
}
res.status(status.Ok);
return res.redirect('/login');
});
});
router.post("/login", async (req, res) => {
const credentialSchema = joi.object({
email: joi.string().email({ minDomainSegments: 2, tlds: { allow: true } }).required(),
password: joi.string().max(20).required(),
});
const valid = credentialSchema.validate(req.body);
if (valid.error) {
req.session.errMessage = "Invalid input:" + valid.error.details.map(d => d.message.replace(/"/g, '')).join(', ');
res.status(status.BadRequest);
return res.redirect("/login");
}
users.findOne({ "email": req.body.email }).then((user) => {
if (!user) {
req.session.errMessage = "User not found";
res.status(status.NotFound);
return res.redirect(`/login/?email=${req.body.email}`);
}
if (!bcrypt.compareSync(req.body.password, user.password)) {
req.session.errMessage = "Incorrect password";
res.status(status.Unauthorized);
return res.redirect(`/login/?email=${req.body.email}`);
}
req.session.authenticated = true;
req.session.userId = user._id;
req.session.errMessage = "";
req.session.save((err) => {
if (err) {
console.error("Failed to save session: ", err);
req.session.errMessage = "Failed to save session. Please try again.";
res.status(status.InternalServerError);
return res.redirect("/login");
}
res.status(status.Ok);
return res.redirect("/home");
});
});
});
router.post("/signup", async (req, res) => {
const userSchema = joi.object({
email: joi.string().email({ minDomainSegments: 2, tlds: { allow: true } }).required(),
name: joi.string().pattern(new RegExp('^[a-zA-Z0-9_ ]*$')).max(20).required(),
password: joi.string().max(20).min(8).required(),
repassword: joi.string().max(20).min(8).required(),
});
const valid = userSchema.validate(req.body);
if (valid.error) {
req.session.errMessage = valid.error.details[0].message,
res.status(status.BadRequest);
return res.redirect("/signup");
}
let exists = await users.findOne({ email: req.body.email }).then((exists) => exists);
if (exists) {
req.session.errMessage = "Email already in use";
res.status(status.BadRequest);
return res.redirect("/signup");
}
if (req.body.password != req.body.repassword) {
req.session.errMessage = "Passwords must match";
res.status(status.BadRequest);
return res.redirect(`/signup/?name=${req.body.name}&email=${req.body.email}`);
}
let strength = passwordStrength(req.body.password);
if (strength.id < 2) {
req.session.errMessage = `Password ${strength.value}`;
res.status(status.BadRequest);
return res.redirect(`/signup/?name=${req.body.name}&email=${req.body.email}`);
}
let hashedPassword = await bcrypt.hashSync(req.body.password, salt);
users.insertOne({
email: req.body.email,
name: req.body.name,
password: hashedPassword,
financialData: false,
}).then((results, err) => {
if (err) {
console.error("Error creating user on signup: ", err);
res.session.errMessage = "Internal server error";
return res.status(status.InternalServerError).redirect("/signup");
}
req.session.authenticated = true;
req.session.userId = results.insertedId;
req.session.errMessage = "";
req.session.save((err) => {
if (err) {
console.error("Failed to save session: ", err);
req.session.errMessage = "Please login";
res.status(status.Ok);
return res.redirect("/login");
}
res.status(status.Ok);
return res.redirect("/home");
});
});
});
return router;
}
+134
View File
@@ -0,0 +1,134 @@
const { passwordStrength } = require("check-password-strength");
const nodeMail = require('nodemailer');
const express = require('express');
const bcrypt = require('bcrypt');
const crypto = require('crypto');
const joi = require('joi');
require('dotenv').config();
const PORT = process.env.PORT;
const transporter = nodeMail.createTransport({
service: 'gmail',
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
}
});
module.exports = (users) => {
const router = express.Router();
router.post('/auth/resetPass', async (req, res) => {
const resetSchema = joi.object({
email: joi.string().email({ minDomainSegments: 2, tlds: { allow: true } }).required(),
});
req.session.error = '';
req.session.reset = '';
const valid = resetSchema.validate(req.body);
if (valid.error) {
req.session.error = 'invalid email';
return res.redirect('/forgotPassword')
}
const { email } = req.body;
const user = await users.findOne({ email });
if (!user) {
req.session.error = 'No user found'
return res.redirect('/forgotPassword');
}
const token = crypto.randomBytes(32).toString('hex');
console.log(`The reset token is ${token}`)
const expiration = Date.now() + 3600000;
await users.updateOne({ email }, {
$set: { resetToken: token, resetTokenExpires: expiration }
});
const resetUrl = `http://localhost:${PORT}/reset/${token}`;
const mailSend = {
from: process.env.EMAIL_USER,
to: email,
subject: 'Password reset',
text: `Hi ${user.name},\nA request was sent to reset your password. If this wasn't you, please ignore this email.\nIf you sent the request, reset your password here ${resetUrl} this link will expire within 1 hour, \n Do not share this link with anyone.
\n \n Thankyou, The RCalculator team.`,
html: `
<p>Hi ${user.name}</p>
<p>A request was sent to reset your password. <strong>If this wasn't you, please ignore this email.</strong></p>
<p>If you sent the request, reset your password <a href="${resetUrl}">here</a>. This link will expire in 1 hour.</p>
<p><img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/rekor.png" alt="wallet icon" width="150"/></p>
<p>Thank you,<br/>The RCalculator team</p>`,
};
try {
await transporter.sendMail(mailSend);
req.session.reset = 'Reset link sent Check your email';
res.redirect('/forgotPassword');
} catch (err) {
console.log('there was an error', err);
res.status(500).send('email failed to send try again');
}
});
//Reset link verifies information from user and checks token
router.post('/resetLink', async (req, res) => {
const { token, password, confirmPassword, } = req.body;
const passwordSchema = joi.object({
password: joi.string().max(20).required(),
confirmPassword: joi.string().max(20).required(),
});
const valid = passwordSchema.validate({ password, confirmPassword });
if (valid.error) {
console.log("houston we have a problem"); // nice
req.session.error = "Invalid input:" + valid.error.details.map(d => d.message.replace(/"/g, '')).join(', ');;
res.status(status.BadRequest);
return res.redirect(`/reset/${token}`);
}
if (!token || !password || !confirmPassword) {
req.session.error = 'field may be missing';
return res.redirect(`/reset/${token}`);
}
if (password !== confirmPassword) {
req.session.error = 'passwords do not match';
return res.redirect(`/reset/${token}`);
}
const user = await users.findOne({
resetToken: token,
resetTokenExpires: { $gt: Date.now() },
});
if (!user) {
req.session.error = 'Reset link is invalid.';
return res.redirect(`/reset`);
}
let strength = passwordStrength(password);
if (strength.id < 2) {
req.session.errMessage = `Password ${strength.value}`;
return res.redirect(`/reset/${token}`);
}
const hashPassword = await bcrypt.hash(password, 12);
await users.updateOne(
{
email: user.email
},
{
$set: {
password: hashPassword,
resetToken: '',
resetTokenExpires: 0,
},
}
);
req.session.success = 'Password has been reset';
res.redirect('/login');
});
return router;
}
+70
View File
@@ -0,0 +1,70 @@
const status = require("../util/statuses");
const session = require("express-session");
const ObjectId = require("mongodb").ObjectId;
// Get all names of user routes
let userRouter = require("../router/user")((req, res, next) => next(), null, null, null);
userRouter.stack.shift();
const userRoutes = userRouter.stack.map((layer) => layer.route.path.split("/")[1]);
/**
* @param {MongoClient.collection} users db collection
* @returns {async function} middleware handler function
*/
const createMiddleware = (users) => {
return async (req, res, next) => {
// Check if incoming request route exists in user routes
// redirect to 404 if not
if (!userRoutes.includes(req.url.split("/")[1].split("?")[0])) {
return res.status(status.NotFound).redirect("/notFound");
}
if (!req.session.authenticated || !req.session.userId) {
req.session.errMessage = "Please login to view that resource";
res.redirect("/login");
return res.status(status.Unauthorized);
}
if (!req.session.user) {
let user = await users.findOne({ _id: new ObjectId(req.session.userId) }).then((user) => user);
if (!user) {
return req.session.destroy((err) => {
if (err) {
console.error("Failed to destroy session: ", err);
req.session.errMessage = "Failed to logout. Please try again";
res.status(status.InternalServerError);
return res.redirect("/home");
}
req.session.errMessage = "User not found";
res.status(status.NotFound);
res.redirect("/login");
});
}
req.session.user = user;
return req.session.save((err) => {
if (err) {
console.error("Failed to save session: ", err);
return req.session.destroy((err) => {
req.session.errMessage = "Failed to save session, please login again.";
if (err) {
console.error("Failed to destroy session: ", err);
}
res.status(status.InternalServerError);
res.redirect("/login");
});
}
next();
});
}
next();
};
};
module.exports = createMiddleware;
+28
View File
@@ -0,0 +1,28 @@
const MongoClient = require("mongodb").MongoClient;
/**
* connectMongo returns a database connection to MongoDB
* @param {string} mongoURI
* @param {string} databaseName
* @return {MongoClient}
*/
const connectMongo = async (mongoURI, databaseName) => {
const database = await MongoClient.connect(mongoURI, { connectTimeoutMS: 1000 });
const dbo = database.db(databaseName);
return dbo;
}
/**
* getCollection object to interact with MongoDB
* @param {MongoClient} dbo
* @param {string} collection
* @return {MongoClient.collection}
*/
const getCollection = async (dbo, collection) => {
return await dbo.collection(collection);
}
module.exports = {
connectMongo: connectMongo,
getCollection: getCollection,
}
File renamed without changes.
+178
View File
@@ -0,0 +1,178 @@
const assetForms = [
"create-other-asset-form",
"create-saving-asset-form",
"create-stock-asset-form"
];
/**
* resetAll asset creation forms
*/
function resetAll() {
document.getElementById("dropdown-icon-button").innerHTML = `
<img src="/static/svgs/icons/Other.svg" class="h-4 w-4 me-2" alt="Other"> Other
<svg class="w-2.5 h-2.5 ms-2.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
</svg>
`;
document.getElementById("create-other-asset-form").reset();
document.getElementById("create-saving-asset-form").reset();
document.getElementById("create-stock-asset-form").reset();
}
/**
* selectAssetForm changes which form is displayed
* to create specified assets.
* @param {string} assetFormId
*/
function selectAssetForm(assetFormId) {
for (let i = 0; i < assetForms.length; i++) {
if (assetForms[i] == assetFormId) {
document.getElementById(assetFormId).style.display = "block";
} else {
document.getElementById(assetForms[i]).style.display = "none";
}
}
}
/**
* resetRadio to default to other asset type.
*/
function resetRadio() {
document.getElementById("asset-other").checked = true;
document.getElementById("asset-saving").checked = false;
document.getElementById("asset-stock").checked = false;
}
const assetKeys = {
other: [
"name",
"dropdown-icon-button",
"value",
"description",
"purchaseDate",
],
saving: [
"name",
"value",
],
stock: [
"ticker",
"price",
"quantity",
"purchaseDate",
],
}
/**
* lockAsset prevents edits to asset view modal
* @param {string} assetId
* @param {string} icon to defualt to
*/
function lockAsset(assetId, icon) {
let dropdown = document.getElementById(`dropdown-icon-button-${assetId}`);
if (dropdown) dropdown.innerHTML = `
<img src="/static/svgs/icons/${icon}.svg" class="h-4 w-4 me-2" alt="${icon}"> ${icon}
<svg class="w-2.5 h-2.5 ms-2.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
</svg>
`;
document.getElementById(`${assetId}-form`).reset();
const type = document.getElementById(`type-${assetId}`).value;
assetKeys[type].forEach((key) => {
document.getElementById(`${key}-${assetId}`).disabled = true;
});
if (icon === "Car" || icon === "Motorcycle") document.getElementById(`year-${assetId}`).disabled = true;
document.getElementById(`save-${assetId}`).disabled = true;
document.getElementById(`save-${assetId}`).classList.remove("cursor-pointer");
document.getElementById(`save-${assetId}`).classList.add("cursor-not-allowed");
document.getElementById(`edit-${assetId}`).innerHTML = "Edit";
document.getElementById(`edit-${assetId}`).onclick = () => { unlockAsset(assetId, icon) };
}
/**
* unlockAsset allows edits to asset view modal
* @param {string} assetId
* @param {string} icon to defualt to
*/
function unlockAsset(assetId, icon) {
const type = document.getElementById(`type-${assetId}`).value;
assetKeys[type].forEach((key) => {
document.getElementById(`${key}-${assetId}`).disabled = false;
});
if (icon === "Car" || icon === "Motorcycle") document.getElementById(`year-${assetId}`).disabled = false;
document.getElementById(`save-${assetId}`).disabled = false;
document.getElementById(`save-${assetId}`).classList.remove("cursor-not-allowed");
document.getElementById(`save-${assetId}`).classList.add("cursor-pointer");
document.getElementById(`edit-${assetId}`).innerHTML = "Cancel";
document.getElementById(`edit-${assetId}`).onclick = () => { lockAsset(assetId, icon) };
}
/**
* autoOpenCreate checks if popup param
* in url to auto open create popup
*/
function autoOpenCreate() {
const query = window.location.search;
const params = new URLSearchParams(query);
if (params.has("popup")) {
document.getElementById('create-asset-modal').showModal();
}
}
/**
* selectIcon updated selected icon while creating
* or modifying assets.
* @param {button element} selectedIcon
* @param {string} assetId
*/
function selectIcon(selectedIcon, assetId="") {
toggleYear(selectedIcon.value, assetId);
document.getElementById(`dropdown-icon-button${assetId != "" ? "-" : ""}${assetId}`).value = selectedIcon.value;
document.getElementById(`icon${assetId != "" ? "-" : ""}${assetId}`).value = selectedIcon.value
document.getElementById(`dropdown-icon-button${assetId != "" ? "-" : ""}${assetId}`).innerHTML = selectedIcon.innerHTML +
`
<svg class="w-2.5 h-2.5 ms-2.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
</svg>
`;
}
/**
* toggleYear shows or hides year input
* @param {string} type of asset
*/
function toggleYear(type, assetId="") {
if (type === "Car" || type === "Motorcycle") {
if (assetId) {
document.getElementById(`year-${assetId}`).disabled = false;
document.getElementById(`year-modify-${assetId}`).style.display = 'block';
} else {
document.getElementById('year-input').disabled = false;
document.getElementById('year-create').style.display = 'block';
}
} else {
if (assetId) {
document.getElementById(`year-${assetId}`).disabled = true;
document.getElementById(`year-modify-${assetId}`).style.display = 'none';
} else {
document.getElementById('year-input').disabled = true;
document.getElementById('year-create').style.display = 'none';
}
}
}
resetRadio();
autoOpenCreate();
+20
View File
@@ -0,0 +1,20 @@
/**
* Function takes in a number and formats it to currency
* @param {number} value
* @returns {string} number formatted in currency
*/
document.addEventListener("DOMContentLoaded", () => {
const number = document.querySelectorAll(".planGoal");
number.forEach((value) => {
num = parseFloat(value.textContent);
if (num >= 999999) {
value.textContent = "$" + (num /= 1000000) + "M";
} else if (num > 999) {
value.textContent = "$" + (num /= 1000) + "k";
} else {
return "$" + num;
}
});
});
+67
View File
@@ -0,0 +1,67 @@
// Basic client-side form submission handler to process JSON response
document.addEventListener('DOMContentLoaded', () => {
const form = document.getElementById('editPlanForm');
const errorMessagesDiv = document.getElementById('formErrorMessages');
// Store initial form values
const initialValues = {
name: form.name.value,
retirementAge: form.retirementAge.value,
retirementExpenses: form.retirementExpenses.value,
retirementAssets: form.retirementAssets.value,
retirementLiabilities: form.retirementLiabilities.value
};
const resetButton = document.getElementById('resetButton');
resetButton.addEventListener('click', function() {
form.name.value = initialValues.name;
form.retirementAge.value = initialValues.retirementAge;
form.retirementExpenses.value = initialValues.retirementExpenses;
form.retirementAssets.value = initialValues.retirementAssets;
form.retirementLiabilities.value = initialValues.retirementLiabilities;
if (errorMessagesDiv) {
errorMessagesDiv.style.display = 'none'; // Hide error message on reset
errorMessagesDiv.textContent = '';
}
});
if (form) {
form.addEventListener('submit', async function(event) {
event.preventDefault(); // Prevent traditional form submission
errorMessagesDiv.textContent = '';
errorMessagesDiv.style.display = 'none';
const formData = new FormData(form);
const data = {};
formData.forEach((value, key) => {
data[key] = value;
});
try {
const response = await fetch(form.action, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
const result = await response.json();
if (response.ok && result.success) {
const actionUrl = form.action;
const urlParts = actionUrl.split('/');
const planId = urlParts[urlParts.length - 2];
window.location.href = `/plans/${planId}`;
} else {
errorMessagesDiv.textContent = result.message || 'An error occurred while updating the plan.';
errorMessagesDiv.style.display = 'block';
}
} catch (error) {
console.error('Error submitting form:', error);
errorMessagesDiv.textContent = 'A network error occurred. Please try again.';
errorMessagesDiv.style.display = 'block';
}
});
}
});
+71
View File
@@ -0,0 +1,71 @@
const factButton = document.getElementById("factButton");
const factMenu = document.getElementById("factMenu");
const factInput = document.getElementById("factInput");
const factSubmitButton = document.getElementById("factSubmitButton");
var path = window.location.pathname;
var page = path.split("/").pop();
if(page !== "login" && page !== "signup" && page !== "forgotPassword" && page !== "") {
factButton.classList.remove("hidden");
}
factButton.addEventListener("click", () => {
factMenu.classList.toggle("hidden");
});
factSubmitButton.addEventListener("click", () => {
fetch("/fact", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
fact: factInput.value,
}),
})
.then(response => response.json())
.then(data => {
Swal.fire('Here is a fact!', data.fact, 'success');
})
.catch(error => {
console.error("Error fetching fact:", error);
Swal.fire('Error fetching fact', 'Please try again later', 'error');
});
});
document.addEventListener("DOMContentLoaded", () => {
const drawer = document.getElementById("drawer-navigation");
const toggle = document.getElementById("drawer-toggle");
function openDrawer() {
drawer.classList.remove("translate-x-full");
// Add backdrop
const backdrop = document.createElement("div");
backdrop.className = "fixed inset-0 bg-gray-900/50 z-30 drawer-backdrop";
document.body.appendChild(backdrop);
// Optional: prevent scroll
document.body.classList.add("overflow-hidden");
backdrop.addEventListener("click", closeDrawer);
}
function closeDrawer() {
drawer.classList.add("translate-x-full");
const backdrop = document.querySelector(".drawer-backdrop");
if (backdrop) backdrop.remove();
document.body.classList.remove("overflow-hidden");
}
toggle.addEventListener("click", openDrawer);
// Also close on X button if needed
const closeBtn = drawer.querySelector("[data-drawer-hide]");
if (closeBtn) {
closeBtn.addEventListener("click", closeDrawer);
}
});
+108
View File
@@ -0,0 +1,108 @@
/**
* getLocation gets the geolocation of user on page load.
*/
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(getLatestExchange, error);
}
}
/**
* update the currency exchange rate dropdown
* or display error
* @param {object} data
*/
function update(data) {
if (data.data.message != "error") {
document.getElementById("loading").style = "display: none";
document.getElementById("currencyExchange").style = "display: flex";
document.getElementById("yourFlag").src = `/static/svgs/flags/${data.data.country}.svg`
document.getElementById("yourFlag").alt = data.data.country;
document.getElementById("flagTag").innerHTML = `(${data.data.country})`;
document.getElementById("exchangeFlag").src = `/static/svgs/flags/USD.svg`;
document.getElementById("exchangeFlag").alt = "USD";
document.getElementById("exFlagTag").innerHTML = "(USD)";
document.getElementById("dropdown-country-button").value = data.data.toCurrencyRates["USD"];
updateExchange(document.getElementById("dropdown-country-button").value);
const dropdown = document.getElementById("dropdown");
let countries = Object.keys(data.data.toCurrencyRates);
countries.forEach(item => {
if (dropdown.querySelector(`button[value="${data.data.toCurrencyRates[item]}"]`)) {
return;
}
const listItem = document.createElement("li");
listItem.innerHTML = `<button onclick="switchButton(this)" type="button" value="${data.data.toCurrencyRates[item]}" class="country Button inline-flex w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-600 dark:hover:text-white" role="menuitem">
<span class="inline-flex items-center">
<img src="/static/svgs/flags/${item}.svg" class="h-4 w-4 me-2" alt="${item}"> (${item})
</span>
</button>`;
dropdown.appendChild(listItem);
});
} else {
document.getElementById("loading").style = "display: none";
document.getElementById("exFrom").style = "display: none";
document.getElementById("dropdown-country-button").style = "display: none";
document.getElementById("currencyExchange").style = "display: flex";
document.getElementById("exchange").innerHTML = "Country not supported";
document.getElementById("exchange").className = "block rounded-lg text-center p-2.5 w-50 z-20 text-sm border bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:border-blue-500";
}
}
/**
* switchButton changes the selected exchange rate icon
* @param {element} clickedButton
*/
function switchButton(clickedButton) {
document.getElementById("dropdown-country-button").value = clickedButton.value;
updateExchange(document.getElementById("dropdown-country-button").value);
document.getElementById("dropdown-country-button").innerHTML = clickedButton.innerHTML +
`
<svg class="w-2.5 h-2.5 ms-2.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
</svg>
`;
}
/**
* updateExchange updates the exchange rate
* @param {number} exRate
*/
function updateExchange(exRate) {
document.getElementById("exchange").innerHTML = "$1.00 = $" +`${(1 * exRate).toFixed(2)}`;
}
/**
* getLatestExchange gets rates from API from a given location.
* @param {object} position coordinates
*/
async function getLatestExchange(position) {
let lat = position.coords.latitude;
let lon = position.coords.longitude;
const res = await fetch(`/exRates/${lat}/${lon}`);
const data = await res.json();
update(data);
}
/**
* error displays error message
* @param {string} err
*/
function error(err) {
const data = {
data: {
message: "error"
}
}
update(data);
}
+47
View File
@@ -0,0 +1,47 @@
/**
* editPlan redirect
* @param {string} planId
*/
function editPlan(planId) {
if (planId) {
window.location.href = `/plans/${planId}/edit`;
} else {
console.error('editPlan called without a planId');
alert('Cannot edit plan: Plan ID is missing.');
}
}
/**
* deletePlan handler
* @param {string} planId
*/
async function deletePlan(planId) {
if (!planId) {
console.error('deletePlan called without a planId');
alert('Cannot delete plan: Plan ID is missing.');
return;
}
if (confirm('Are you sure you want to delete this plan? This action cannot be undone.')) {
try {
const response = await fetch(`/plans/${planId}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
}
});
const result = await response.json();
if (response.ok && result.success) {
alert('Plan deleted successfully.');
window.location.href = '/plans'; // Redirect to the plans list page
} else {
alert(`Failed to delete plan: ${result.message || 'Unknown error'}`);
}
} catch (error) {
console.error('Error deleting plan:', error);
alert('An error occurred while trying to delete the plan. Please check the console for details and ensure the server is running.');
}
}
}
+85
View File
@@ -0,0 +1,85 @@
document.addEventListener('DOMContentLoaded', () => {
const openModalBtn = document.getElementById('openNewPlanModalBtn');
const newPlanDialog = document.getElementById('newPlanDialog');
const newPlanForm = document.getElementById('newPlanForm');
const submitNewPlanBtn = document.getElementById('submitNewPlanBtn');
const newPlanError = document.getElementById('newPlanError');
const resetNewPlanFormBtn = document.getElementById('resetNewPlanFormBtn');
let url = new URLSearchParams(window.location.search);
let modalValue = url.get('openModal');
if (modalValue === 'true') {
newPlanDialog.showModal();
}
openModalBtn.addEventListener('click', () => {
newPlanDialog.showModal();
// Error reset is now handled by 'close' event, but good to clear on open too
newPlanError.style.display = 'none';
newPlanError.textContent = '';
});
// Handles form reset and error clearing when dialog is closed by any means (Esc, Cancel button, backdrop click, successful submit)
newPlanDialog.addEventListener('close', () => {
newPlanForm.reset();
newPlanError.style.display = 'none';
newPlanError.textContent = '';
});
// Close dialog if user clicks on the backdrop
newPlanDialog.addEventListener('click', (event) => {
if (event.target === newPlanDialog) {
newPlanDialog.close(); // This will trigger the 'close' event listener above
}
});
resetNewPlanFormBtn.addEventListener('click', () => {
newPlanForm.reset();
newPlanError.style.display = 'none';
newPlanError.textContent = '';
});
newPlanForm.addEventListener('submit', async (event) => {
event.preventDefault();
submitNewPlanBtn.disabled = true;
submitNewPlanBtn.textContent = 'Saving...';
newPlanError.style.display = 'none';
const formData = new FormData(newPlanForm);
const data = Object.fromEntries(formData.entries());
try {
const response = await fetch('/newPlan', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (response.ok) {
const result = await response.json();
if (result.success) {
newPlanDialog.close(); // This will trigger the 'close' event listener
window.location.reload(); // Reload to see the new plan
} else {
newPlanError.textContent = result.message || 'Failed to create plan. Please try again.';
newPlanError.style.display = 'block';
}
} else {
const errorData = await response.json();
newPlanError.textContent = errorData.message || `Error: ${response.status} - ${response.statusText}`;
newPlanError.style.display = 'block';
}
} catch (error) {
console.error('Error submitting new plan:', error);
newPlanError.textContent = 'An unexpected error occurred. Please try again.';
newPlanError.style.display = 'block';
}
finally {
submitNewPlanBtn.disabled = false;
submitNewPlanBtn.textContent = 'Save Plan';
}
});
});
+87
View File
@@ -0,0 +1,87 @@
/**
* lockAccount resets inputs and disabled inputs
*/
function lockAccount() {
// Clear unsaved inputs on page load (refresh doesnt clear them)
document.getElementById("account-form").reset();
document.getElementById("save-account").disabled = true;
document.getElementById("email").disabled = true;
document.getElementById("name").disabled = true;
document.getElementById("password").disabled = true;
document.getElementById("repassword").disabled = true;
document.getElementById("save-account").classList.add("cursor-not-allowed");
document.getElementById("save-account").classList.remove("cursor-pointer");
document.getElementById("edit-account").innerHTML = "Edit";
document.getElementById("edit-account").onclick = unlockAccount;
}
/**
* lockPersonal resets inputs and disabled inputs
*/
function lockPersonal() {
// Clear unsaved inputs on page load (refresh doesnt clear them)
document.getElementById("personal-form").reset();
document.getElementById("save-personal").disabled = true;
document.getElementById("dob").disabled = true;
document.getElementById("education").disabled = true;
document.getElementById("ms-single").disabled = true;
document.getElementById("ms-married").disabled = true;
document.getElementById("ms-divorced").disabled = true;
document.getElementById("ms-widowed").disabled = true;
document.getElementById("income").disabled = true;
document.getElementById("expenses").disabled = true;
document.getElementById("assets").disabled = true;
document.getElementById("liabilities").disabled = true;
document.getElementById("save-personal").classList.add("cursor-not-allowed");
document.getElementById("save-personal").classList.remove("cursor-pointer");
document.getElementById("edit-personal").innerHTML = "Edit";
document.getElementById("edit-personal").onclick = unlockPersonal;
}
/**
* unlockAccount removes disabled from inputs and
* allows users to edit their profile.
*/
function unlockAccount() {
document.getElementById("save-account").disabled = false;
document.getElementById("email").disabled = false;
document.getElementById("name").disabled = false;
document.getElementById("password").disabled = false;
document.getElementById("repassword").disabled = false;
document.getElementById("save-account").classList.remove("cursor-not-allowed");
document.getElementById("save-account").classList.add("cursor-pointer");
document.getElementById("edit-account").innerHTML = "Cancel changes";
document.getElementById("edit-account").onclick = lockAccount;
}
/**
* unlocPersonal removes disabled from inputs and
* allows users to edit their personal information.
*/
function unlockPersonal() {
document.getElementById("save-personal").disabled = false;
document.getElementById("dob").disabled = false;
document.getElementById("education").disabled = false;
document.getElementById("ms-single").disabled = false;
document.getElementById("ms-married").disabled = false;
document.getElementById("ms-divorced").disabled = false;
document.getElementById("ms-widowed").disabled = false;
document.getElementById("income").disabled = false;
document.getElementById("expenses").disabled = false;
document.getElementById("assets").disabled = false;
document.getElementById("liabilities").disabled = false;
document.getElementById("save-personal").classList.remove("cursor-not-allowed");
document.getElementById("save-personal").classList.add("cursor-pointer");
document.getElementById("edit-personal").innerHTML = "Cancel changes";
document.getElementById("edit-personal").onclick = lockPersonal;
}
// On page load, ensure forms are locked and reset
lockAccount();
lockPersonal();
+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M10.464 8.746c.227-.18.497-.311.786-.394v2.795a2.252 2.252 0 0 1-.786-.393c-.394-.313-.546-.681-.546-1.004 0-.323.152-.691.546-1.004ZM12.75 15.662v-2.824c.347.085.664.228.921.421.427.32.579.686.579.991 0 .305-.152.671-.579.991a2.534 2.534 0 0 1-.921.42Z" />
<path fill-rule="evenodd" d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25ZM12.75 6a.75.75 0 0 0-1.5 0v.816a3.836 3.836 0 0 0-1.72.756c-.712.566-1.112 1.35-1.112 2.178 0 .829.4 1.612 1.113 2.178.502.4 1.102.647 1.719.756v2.978a2.536 2.536 0 0 1-.921-.421l-.879-.66a.75.75 0 0 0-.9 1.2l.879.66c.533.4 1.169.645 1.821.75V18a.75.75 0 0 0 1.5 0v-.81a4.124 4.124 0 0 0 1.821-.749c.745-.559 1.179-1.344 1.179-2.191 0-.847-.434-1.632-1.179-2.191a4.122 4.122 0 0 0-1.821-.75V8.354c.29.082.559.213.786.393l.415.33a.75.75 0 0 0 .933-1.175l-.415-.33a3.836 3.836 0 0 0-1.719-.755V6Z" clip-rule="evenodd" />
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M11.293 3.293a1 1 0 0 1 1.414 0l6 6 2 2a1 1 0 0 1-1.414 1.414L19 12.414V19a2 2 0 0 1-2 2h-3a1 1 0 0 1-1-1v-3h-2v3a1 1 0 0 1-1 1H7a2 2 0 0 1-2-2v-6.586l-.293.293a1 1 0 0 1-1.414-1.414l2-2 6-6Z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 358 B

+8
View File
@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-au" viewBox="0 0 640 480">
<path fill="#00008B" d="M0 0h640v480H0z"/>
<path fill="#fff" d="m37.5 0 122 90.5L281 0h39v31l-120 89.5 120 89V240h-40l-120-89.5L40.5 240H0v-30l119.5-89L0 32V0z"/>
<path fill="red" d="M212 140.5 320 220v20l-135.5-99.5zm-92 10 3 17.5-96 72H0zM320 0v1.5l-124.5 94 1-22L295 0zM0 0l119.5 88h-30L0 21z"/>
<path fill="#fff" d="M120.5 0v240h80V0zM0 80v80h320V80z"/>
<path fill="red" d="M0 96.5v48h320v-48zM136.5 0v240h48V0z"/>
<path fill="#fff" d="m527 396.7-20.5 2.6 2.2 20.5-14.8-14.4-14.7 14.5 2-20.5-20.5-2.4 17.3-11.2-10.9-17.5 19.6 6.5 6.9-19.5 7.1 19.4 19.5-6.7-10.7 17.6zm-3.7-117.2 2.7-13-9.8-9 13.2-1.5 5.5-12.1 5.5 12.1 13.2 1.5-9.8 9 2.7 13-11.6-6.6zm-104.1-60-20.3 2.2 1.8 20.3-14.4-14.5-14.8 14.1 2.4-20.3-20.2-2.7 17.3-10.8-10.5-17.5 19.3 6.8L387 178l6.7 19.3 19.4-6.3-10.9 17.3 17.1 11.2ZM623 186.7l-20.9 2.7 2.3 20.9-15.1-14.7-15 14.8 2.1-21-20.9-2.4 17.7-11.5-11.1-17.9 20 6.7 7-19.8 7.2 19.8 19.9-6.9-11 18zm-96.1-83.5-20.7 2.3 1.9 20.8-14.7-14.8-15.1 14.4 2.4-20.7-20.7-2.8 17.7-11L467 73.5l19.7 6.9 7.3-19.5 6.8 19.7 19.8-6.5-11.1 17.6zM234 385.7l-45.8 5.4 4.6 45.9-32.8-32.4-33 32.2 4.9-45.9-45.8-5.8 38.9-24.8-24-39.4 43.6 15 15.8-43.4 15.5 43.5 43.7-14.7-24.3 39.2 38.8 25.1Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-bg" viewBox="0 0 640 480">
<path fill="#fff" d="M0 0h640v160H0z"/>
<path fill="#00966e" d="M0 160h640v160H0z"/>
<path fill="#d62612" d="M0 320h640v160H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 225 B

+45
View File
@@ -0,0 +1,45 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-br" viewBox="0 0 640 480">
<g stroke-width="1pt">
<path fill="#229e45" fill-rule="evenodd" d="M0 0h640v480H0z"/>
<path fill="#f8e509" fill-rule="evenodd" d="m321.4 436 301.5-195.7L319.6 44 17.1 240.7z"/>
<path fill="#2b49a3" fill-rule="evenodd" d="M452.8 240c0 70.3-57.1 127.3-127.6 127.3A127.4 127.4 0 1 1 452.8 240"/>
<path fill="#ffffef" fill-rule="evenodd" d="m283.3 316.3-4-2.3-4 2 .9-4.5-3.2-3.4 4.5-.5 2.2-4 1.9 4.2 4.4.8-3.3 3m86 26.3-3.9-2.3-4 2 .8-4.5-3.1-3.3 4.5-.5 2.1-4.1 2 4.2 4.4.8-3.4 3.1m-36.2-30-3.4-2-3.5 1.8.8-3.9-2.8-2.9 4-.4 1.8-3.6 1.6 3.7 3.9.7-3 2.7m87-8.5-3.4-2-3.5 1.8.8-3.9-2.7-2.8 3.9-.4 1.8-3.5 1.6 3.6 3.8.7-2.9 2.6m-87.3-22-4-2.2-4 2 .8-4.6-3.1-3.3 4.5-.5 2.1-4.1 2 4.2 4.4.8-3.4 3.2m-104.6-35-4-2.2-4 2 1-4.6-3.3-3.3 4.6-.5 2-4.1 2 4.2 4.4.8-3.3 3.1m13.3 57.2-4-2.3-4 2 .9-4.5-3.2-3.3 4.5-.6 2.1-4 2 4.2 4.4.8-3.3 3.1m132-67.3-3.6-2-3.6 1.8.8-4-2.8-3 4-.5 1.9-3.6 1.7 3.8 4 .7-3 2.7m-6.7 38.3-2.7-1.6-2.9 1.4.6-3.2-2.2-2.3 3.2-.4 1.5-2.8 1.3 3 3 .5-2.2 2.2m-142.2 50.4-2.7-1.5-2.7 1.3.6-3-2.1-2.2 3-.4 1.4-2.7 1.3 2.8 3 .6-2.3 2M419 299.8l-2.2-1.1-2.2 1 .5-2.3-1.7-1.6 2.4-.3 1.2-2 1 2 2.5.5-1.9 1.5"/>
<path fill="#ffffef" fill-rule="evenodd" d="m219.3 287.6-2.7-1.5-2.7 1.3.6-3-2.1-2.2 3-.4 1.4-2.7 1.3 2.8 3 .6-2.3 2"/>
<path fill="#ffffef" fill-rule="evenodd" d="m219.3 287.6-2.7-1.5-2.7 1.3.6-3-2.1-2.2 3-.4 1.4-2.7 1.3 2.8 3 .6-2.3 2m42.3 3-2.6-1.4-2.7 1.3.6-3-2.1-2.2 3-.4 1.4-2.7 1.3 2.8 3 .5-2.3 2.1m-4.8 17-2.6-1.5-2.7 1.4.6-3-2.1-2.3 3-.4 1.4-2.7 1.3 2.8 3 .6-2.3 2m87.4-22.2-2.6-1.6-2.8 1.4.6-3-2-2.3 3-.3 1.4-2.7 1.2 2.8 3 .5-2.2 2.1m-25.1 3-2.7-1.5-2.7 1.4.6-3-2-2.3 3-.3 1.4-2.8 1.2 2.9 3 .5-2.2 2.1m-68.8-5.8-1.7-1-1.7.8.4-1.9-1.3-1.4 1.9-.2.8-1.7.8 1.8 1.9.3-1.4 1.3m167.8 45.4-2.6-1.5-2.7 1.4.6-3-2.1-2.3 3-.4 1.4-2.7 1.3 2.8 3 .6-2.3 2m-20.8 6-2.2-1.4-2.3 1.2.5-2.6-1.7-1.8 2.5-.3 1.2-2.3 1 2.4 2.5.4-1.9 1.8m10.4 2.3-2-1.2-2.1 1 .4-2.3-1.6-1.7 2.3-.3 1.1-2 1 2 2.3.5-1.7 1.6m29.1-22.8-2-1-2 1 .5-2.3-1.6-1.7 2.3-.3 1-2 1 2.1 2.1.4-1.6 1.6m-38.8 41.8-2.5-1.4-2.7 1.2.6-2.8-2-2 3-.3 1.3-2.5 1.2 2.6 3 .5-2.3 1.9m.6 14.2-2.4-1.4-2.4 1.3.6-2.8-1.9-2 2.7-.4 1.2-2.5 1.1 2.6 2.7.5-2 2m-19-23.1-1.9-1.2-2 1 .4-2.2-1.5-1.7 2.2-.2 1-2 1 2 2.2.4-1.6 1.6m-17.8 2.3-2-1.2-2 1 .5-2.2-1.6-1.7 2.3-.2 1-2 1 2 2.1.4-1.6 1.6m-30.4-24.6-2-1.1-2 1 .5-2.3-1.6-1.6 2.2-.3 1-2 1 2 2.2.5-1.6 1.5m3.7 57-1.6-.9-1.8.9.4-2-1.3-1.4 1.9-.2.9-1.7.8 1.8 1.9.3-1.4 1.3m-46.2-86.6-4-2.3-4 2 .9-4.5-3.2-3.3 4.5-.6 2.2-4 1.9 4.2 4.4.8-3.3 3.1"/>
<path fill="#fff" fill-rule="evenodd" d="M444.4 285.8a125 125 0 0 0 5.8-19.8c-67.8-59.5-143.3-90-238.7-83.7a125 125 0 0 0-8.5 20.9c113-10.8 196 39.2 241.4 82.6"/>
<path fill="#309e3a" d="m414 252.4 2.3 1.3a3 3 0 0 0-.3 2.2 3 3 0 0 0 1.4 1.7q1 .8 2 .7.9 0 1.3-.7l.2-.9-.5-1q-.3-.5-1.5-1.8a8 8 0 0 1-1.8-3 4 4 0 0 1 2-4.4 4 4 0 0 1 2.3-.2 7 7 0 0 1 2.6 1.2q2.1 1.5 2.6 3.2a4 4 0 0 1-.6 3.3l-2.4-1.5q.5-1 .2-1.7-.2-.8-1.2-1.4a3 3 0 0 0-1.8-.7 1 1 0 0 0-.9.5q-.3.4-.1 1 .2.8 1.6 2.2t2 2.5a4 4 0 0 1-.3 4.2 4 4 0 0 1-1.9 1.5 4 4 0 0 1-2.4.3q-1.3-.3-2.8-1.3-2.2-1.5-2.7-3.3a5 5 0 0 1 .6-4zm-11.6-7.6 2.5 1.3a3 3 0 0 0-.2 2.2 3 3 0 0 0 1.4 1.6q1.1.8 2 .6.9 0 1.3-.8l.2-.8q0-.5-.5-1l-1.6-1.8q-1.7-1.6-2-2.8a4 4 0 0 1 .4-3.1 4 4 0 0 1 1.6-1.4 4 4 0 0 1 2.2-.3 7 7 0 0 1 2.6 1q2.3 1.5 2.7 3.1a4 4 0 0 1-.4 3.4l-2.5-1.4q.5-1 .2-1.7-.4-1-1.3-1.4a3 3 0 0 0-1.9-.6 1 1 0 0 0-.8.5q-.3.4-.1 1 .3.8 1.7 2.2 1.5 1.5 2 2.4a4 4 0 0 1 0 4.2 4 4 0 0 1-1.8 1.6 4 4 0 0 1-2.4.3 8 8 0 0 1-2.9-1.1 6 6 0 0 1-2.8-3.2 5 5 0 0 1 .4-4m-14.2-3.8 7.3-12 8.8 5.5-1.2 2-6.4-4-1.6 2.7 6 3.7-1.3 2-6-3.7-2 3.3 6.7 4-1.2 2zm-20.7-17 1.1-2 5.4 2.7-2.5 5q-1.2.3-3 .2a9 9 0 0 1-3.3-1 8 8 0 0 1-3-2.6 6 6 0 0 1-1-3.5 9 9 0 0 1 1-3.7 8 8 0 0 1 2.6-3 6 6 0 0 1 3.6-1.1q1.4 0 3.2 1 2.4 1.1 3.1 2.8a5 5 0 0 1 .3 3.5l-2.7-.8a3 3 0 0 0-.2-2q-.4-.9-1.6-1.4a4 4 0 0 0-3.1-.3q-1.5.5-2.6 2.6t-.7 3.8a4 4 0 0 0 2 2.4q.8.5 1.7.5h1.8l.8-1.6zm-90.2-22.3 2-14 4.2.7 1.1 9.8 3.9-9 4.2.6-2 13.8-2.7-.4 1.7-10.9-4.4 10.5-2.7-.4-1.1-11.3-1.6 11zm-14.1-1.7 1.3-14 10.3 1-.2 2.4-7.5-.7-.3 3 7 .7-.3 2.4-7-.7-.3 3.8 7.8.7-.2 2.4z"/>
<g stroke-opacity=".5">
<path fill="#309e3a" d="M216.5 191.3q0-2.2.7-3.6a7 7 0 0 1 1.4-1.9 5 5 0 0 1 1.8-1.2q1.5-.5 3-.5 3.1.1 5 2a7 7 0 0 1 1.6 5.5q0 3.3-2 5.3a7 7 0 0 1-5 1.7 7 7 0 0 1-4.8-2 7 7 0 0 1-1.7-5.3"/>
<path fill="#f7ffff" d="M219.4 191.3q0 2.3 1 3.6t2.8 1.3a4 4 0 0 0 2.8-1.1q1-1.2 1.1-3.7.1-2.4-1-3.6a4 4 0 0 0-2.7-1.3 4 4 0 0 0-2.8 1.2q-1.1 1.2-1.2 3.6"/>
</g>
<g stroke-opacity=".5">
<path fill="#309e3a" d="m233 198.5.2-14h6q2.2 0 3.2.5 1 .3 1.6 1.3c.6 1 .6 1.4.6 2.3a4 4 0 0 1-1 2.6 5 5 0 0 1-2.7 1.2l1.5 1.2q.6.6 1.5 2.3l1.7 2.8h-3.4l-2-3.2-1.4-2-.9-.6-1.4-.2h-.6v5.8z"/>
<path fill="#fff" d="M236 190.5h2q2.1 0 2.6-.2.5-.1.8-.5.4-.6.3-1 0-.9-.4-1.2-.3-.4-1-.6h-2l-2.3-.1z"/>
</g>
<g stroke-opacity=".5">
<path fill="#309e3a" d="m249 185.2 5.2.3q1.7 0 2.6.3a5 5 0 0 1 2 1.4 6 6 0 0 1 1.2 2.4q.4 1.4.3 3.3a9 9 0 0 1-.5 3q-.6 1.5-1.7 2.4a5 5 0 0 1-2 1q-1 .3-2.5.2l-5.3-.3z"/>
<path fill="#fff" d="m251.7 187.7-.5 9.3h3.8q.8 0 1.2-.5.5-.4.8-1.3t.4-2.6l-.1-2.5a3 3 0 0 0-.8-1.4l-1.2-.7-2.3-.3z"/>
</g>
<g stroke-opacity=".5">
<path fill="#309e3a" d="m317.6 210.2 3.3-13.6 4.4 1 3.2 1q1.1.6 1.6 1.9t.2 2.8q-.3 1.2-1 2a4 4 0 0 1-3 1.4q-1 0-3-.5l-1.7-.5-1.2 5.2z"/>
<path fill="#fff" d="m323 199.6-.8 3.8 1.5.4q1.6.4 2.2.3a2 2 0 0 0 1.6-1.5q0-.7-.2-1.3a2 2 0 0 0-1-.9l-1.9-.5-1.3-.3z"/>
</g>
<g stroke-opacity=".5">
<path fill="#309e3a" d="m330.6 214.1 4.7-13.2 5.5 2q2.2.8 3 1.4.8.7 1 1.8c.2 1.1.2 1.5 0 2.3q-.6 1.5-1.8 2.2-1.2.6-3 .3.6.7 1 1.6l.8 2.7.6 3.1-3.1-1.1-1-3.6-.7-2.4-.6-.8q-.3-.4-1.3-.7l-.5-.2-2 5.6z"/>
<path fill="#fff" d="m336 207.4 1.9.7q2 .7 2.5.7t.9-.3q.5-.3.6-.9.3-.6 0-1.2a2 2 0 0 0-.8-.9l-2-.7-2-.7-1.2 3.3z"/>
</g>
<g stroke-opacity=".5">
<path fill="#309e3a" d="M347 213.6a9 9 0 0 1 1.7-3.2 7 7 0 0 1 1.8-1.5l2-.7q1.5-.1 3.1.4a7 7 0 0 1 4.2 3.3q1.2 2.4.2 5.7a7 7 0 0 1-3.4 4.5q-2.3 1.3-5.2.4a7 7 0 0 1-4.2-3.3 7 7 0 0 1-.2-5.6"/>
<path fill="#fff" d="M349.8 214.4q-.7 2.3 0 3.8c.7 1.5 1.2 1.6 2.3 2q1.5.5 3-.4 1.4-.8 2.1-3.2.8-2.2 0-3.7a4 4 0 0 0-2.2-2 4 4 0 0 0-3 .3q-1.5.8-2.2 3.2"/>
</g>
<g stroke-opacity=".5">
<path fill="#309e3a" d="m374.3 233.1 6.4-12.4 5.3 2.7a10 10 0 0 1 2.7 1.9q.8.7.8 1.9c0 1.2 0 1.5-.4 2.2a4 4 0 0 1-2 2q-1.5.4-3.1-.2.6 1 .8 1.7.3.9.4 2.8l.2 3.2-3-1.5-.4-3.7-.3-2.5-.5-1-1.2-.7-.5-.3-2.7 5.2z"/>
<path fill="#fff" d="m380.5 227.2 1.9 1q1.8 1 2.3 1t1-.2q.4-.2.7-.8t.2-1.2l-.7-1-1.8-1-2-1z"/>
</g>
<g stroke-opacity=".5">
<path fill="#309e3a" d="M426.1 258.7a9 9 0 0 1 2.5-2.6 7 7 0 0 1 2.2-.9 6 6 0 0 1 2.2 0q1.5.3 2.8 1.2a7 7 0 0 1 3 4.4q.4 2.6-1.4 5.5a7 7 0 0 1-4.5 3.3 7 7 0 0 1-5.2-1.1 7 7 0 0 1-3-4.4q-.4-2.7 1.4-5.4"/>
<path fill="#fff" d="M428.6 260.3q-1.4 2-1.1 3.6a4 4 0 0 0 1.6 2.5q1.5 1 3 .6t2.9-2.4q1.4-2.1 1.1-3.6t-1.6-2.6c-1.4-1.1-2-.8-3-.5q-1.5.3-3 2.4z"/>
</g>
<path fill="#309e3a" d="m301.8 204.5 2.3-9.8 7.2 1.7-.3 1.6-5.3-1.2-.5 2.2 4.9 1.1-.4 1.7-4.9-1.2-.6 2.7 5.5 1.3-.4 1.6z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.0 KiB

+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ca" viewBox="0 0 640 480">
<path fill="#fff" d="M150.1 0h339.7v480H150z"/>
<path fill="#d52b1e" d="M-19.7 0h169.8v480H-19.7zm509.5 0h169.8v480H489.9zM201 232l-13.3 4.4 61.4 54c4.7 13.7-1.6 17.8-5.6 25l66.6-8.4-1.6 67 13.9-.3-3.1-66.6 66.7 8c-4.1-8.7-7.8-13.3-4-27.2l61.3-51-10.7-4c-8.8-6.8 3.8-32.6 5.6-48.9 0 0-35.7 12.3-38 5.8l-9.2-17.5-32.6 35.8c-3.5.9-5-.5-5.9-3.5l15-74.8-23.8 13.4q-3.2 1.3-5.2-2.2l-23-46-23.6 47.8q-2.8 2.5-5 .7L264 130.8l13.7 74.1c-1.1 3-3.7 3.8-6.7 2.2l-31.2-35.3c-4 6.5-6.8 17.1-12.2 19.5s-23.5-4.5-35.6-7c4.2 14.8 17 39.6 9 47.7"/>
</svg>

After

Width:  |  Height:  |  Size: 625 B

+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ch" viewBox="0 0 640 480">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="red" d="M0 0h640v480H0z"/>
<g fill="#fff">
<path d="M170 195h300v90H170z"/>
<path d="M275 90h90v300h-90z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 290 B

+11
View File
@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-cn" viewBox="0 0 640 480">
<defs>
<path id="cn-a" fill="#ff0" d="M-.6.8 0-1 .6.8-1-.3h2z"/>
</defs>
<path fill="#ee1c25" d="M0 0h640v480H0z"/>
<use xlink:href="#cn-a" width="30" height="20" transform="matrix(71.9991 0 0 72 120 120)"/>
<use xlink:href="#cn-a" width="30" height="20" transform="matrix(-12.33562 -20.5871 20.58684 -12.33577 240.3 48)"/>
<use xlink:href="#cn-a" width="30" height="20" transform="matrix(-3.38573 -23.75998 23.75968 -3.38578 288 95.8)"/>
<use xlink:href="#cn-a" width="30" height="20" transform="matrix(6.5991 -23.0749 23.0746 6.59919 288 168)"/>
<use xlink:href="#cn-a" width="30" height="20" transform="matrix(14.9991 -18.73557 18.73533 14.99929 240 216)"/>
</svg>

After

Width:  |  Height:  |  Size: 813 B

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-cz" viewBox="0 0 640 480">
<path fill="#fff" d="M0 0h640v240H0z"/>
<path fill="#d7141a" d="M0 240h640v240H0z"/>
<path fill="#11457e" d="M360 240 0 0v480z"/>
</svg>

After

Width:  |  Height:  |  Size: 225 B

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-dk" viewBox="0 0 640 480">
<path fill="#c8102e" d="M0 0h640.1v480H0z"/>
<path fill="#fff" d="M205.7 0h68.6v480h-68.6z"/>
<path fill="#fff" d="M0 205.7h640.1v68.6H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 236 B

+28
View File
@@ -0,0 +1,28 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-eu" viewBox="0 0 640 480">
<defs>
<g id="eu-d">
<g id="eu-b">
<path id="eu-a" d="m0-1-.3 1 .5.1z"/>
<use xlink:href="#eu-a" transform="scale(-1 1)"/>
</g>
<g id="eu-c">
<use xlink:href="#eu-b" transform="rotate(72)"/>
<use xlink:href="#eu-b" transform="rotate(144)"/>
</g>
<use xlink:href="#eu-c" transform="scale(-1 1)"/>
</g>
</defs>
<path fill="#039" d="M0 0h640v480H0z"/>
<g fill="#fc0" transform="translate(320 242.3)scale(23.7037)">
<use xlink:href="#eu-d" width="100%" height="100%" y="-6"/>
<use xlink:href="#eu-d" width="100%" height="100%" y="6"/>
<g id="eu-e">
<use xlink:href="#eu-d" width="100%" height="100%" x="-6"/>
<use xlink:href="#eu-d" width="100%" height="100%" transform="rotate(-144 -2.3 -2.1)"/>
<use xlink:href="#eu-d" width="100%" height="100%" transform="rotate(144 -2.1 -2.3)"/>
<use xlink:href="#eu-d" width="100%" height="100%" transform="rotate(72 -4.7 -2)"/>
<use xlink:href="#eu-d" width="100%" height="100%" transform="rotate(72 -5 .5)"/>
</g>
<use xlink:href="#eu-e" width="100%" height="100%" transform="scale(-1 1)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-gb" viewBox="0 0 640 480">
<path fill="#012169" d="M0 0h640v480H0z"/>
<path fill="#FFF" d="m75 0 244 181L562 0h78v62L400 241l240 178v61h-80L320 301 81 480H0v-60l239-178L0 64V0z"/>
<path fill="#C8102E" d="m424 281 216 159v40L369 281zm-184 20 6 35L54 480H0zM640 0v3L391 191l2-44L590 0zM0 0l239 176h-60L0 42z"/>
<path fill="#FFF" d="M241 0v480h160V0zM0 160v160h640V160z"/>
<path fill="#C8102E" d="M0 193v96h640v-96zM273 0v480h96V0z"/>
</svg>

After

Width:  |  Height:  |  Size: 504 B

+8
View File
@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-hk" viewBox="0 0 640 480">
<path fill="#EC1B2E" d="M0 0h640v480H0"/>
<path id="hk-a" fill="#fff" d="M346.3 103.1C267 98 230.6 201.9 305.6 240.3c-26-22.4-20.6-55.3-10.1-72.4l1.9 1.1c-13.8 23.5-11.2 52.7 11.1 71-12.7-12.3-9.5-39 12.1-48.9s23.6-39.3 16.4-49.1q-14.7-25.6 9.3-38.9M307.9 164l-4.7 7.4-1.8-8.6-8.6-2.3 7.8-4.3-.6-8.9 6.5 6.1 8.3-3.3-3.7 8.1 5.6 6.8z"/>
<use xlink:href="#hk-a" transform="rotate(72 312.5 243.5)"/>
<use xlink:href="#hk-a" transform="rotate(144 312.5 243.5)"/>
<use xlink:href="#hk-a" transform="rotate(216 312.5 243.5)"/>
<use xlink:href="#hk-a" transform="rotate(288 312.5 243.5)"/>
</svg>

After

Width:  |  Height:  |  Size: 727 B

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-hu" viewBox="0 0 640 480">
<g fill-rule="evenodd">
<path fill="#fff" d="M640 480H0V0h640z"/>
<path fill="#388d00" d="M640 480H0V320h640z"/>
<path fill="#d43516" d="M640 160.1H0V.1h640z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 271 B

+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-id" viewBox="0 0 640 480">
<path fill="#e70011" d="M0 0h640v240H0Z"/>
<path fill="#fff" d="M0 240h640v240H0Z"/>
</svg>

After

Width:  |  Height:  |  Size: 178 B

+14
View File
@@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-il" viewBox="0 0 640 480">
<defs>
<clipPath id="il-a">
<path fill-opacity=".7" d="M-87.6 0H595v512H-87.6z"/>
</clipPath>
</defs>
<g fill-rule="evenodd" clip-path="url(#il-a)" transform="translate(82.1)scale(.94)">
<path fill="#fff" d="M619.4 512H-112V0h731.4z"/>
<path fill="#0038b8" d="M619.4 115.2H-112V48h731.4zm0 350.5H-112v-67.2h731.4zm-483-275 110.1 191.6L359 191.6z"/>
<path fill="#fff" d="m225.8 317.8 20.9 35.5 21.4-35.3z"/>
<path fill="#0038b8" d="M136 320.6 246.2 129l112.4 190.8z"/>
<path fill="#fff" d="m225.8 191.6 20.9-35.5 21.4 35.4zM182 271.1l-21.7 36 41-.1-19.3-36zm-21.3-66.5 41.2.3-19.8 36.3zm151.2 67 20.9 35.5-41.7-.5zm20.5-67-41.2.3 19.8 36.3zm-114.3 0L189.7 256l28.8 50.3 52.8 1.2 32-51.5-29.6-52z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 834 B

+25
View File
@@ -0,0 +1,25 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-in" viewBox="0 0 640 480">
<path fill="#f93" d="M0 0h640v160H0z"/>
<path fill="#fff" d="M0 160h640v160H0z"/>
<path fill="#128807" d="M0 320h640v160H0z"/>
<g transform="matrix(3.2 0 0 3.2 320 240)">
<circle r="20" fill="#008"/>
<circle r="17.5" fill="#fff"/>
<circle r="3.5" fill="#008"/>
<g id="in-d">
<g id="in-c">
<g id="in-b">
<g id="in-a" fill="#008">
<circle r=".9" transform="rotate(7.5 -8.8 133.5)"/>
<path d="M0 17.5.6 7 0 2l-.6 5z"/>
</g>
<use xlink:href="#in-a" width="100%" height="100%" transform="rotate(15)"/>
</g>
<use xlink:href="#in-b" width="100%" height="100%" transform="rotate(30)"/>
</g>
<use xlink:href="#in-c" width="100%" height="100%" transform="rotate(60)"/>
</g>
<use xlink:href="#in-d" width="100%" height="100%" transform="rotate(120)"/>
<use xlink:href="#in-d" width="100%" height="100%" transform="rotate(-120)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+12
View File
@@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-is" viewBox="0 0 640 480">
<defs>
<clipPath id="is-a">
<path fill-opacity=".7" d="M0 0h640v480H0z"/>
</clipPath>
</defs>
<g fill-rule="evenodd" stroke-width="0" clip-path="url(#is-a)">
<path fill="#003897" d="M0 0h666.7v480H0z"/>
<path fill="#fff" d="M0 186.7h186.7V0h106.6v186.7h373.4v106.6H293.3V480H186.7V293.3H0z"/>
<path fill="#d72828" d="M0 213.3h213.3V0h53.4v213.3h400v53.4h-400V480h-53.4V266.7H0z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 509 B

+11
View File
@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-jp" viewBox="0 0 640 480">
<defs>
<clipPath id="jp-a">
<path fill-opacity=".7" d="M-88 32h640v480H-88z"/>
</clipPath>
</defs>
<g fill-rule="evenodd" stroke-width="1pt" clip-path="url(#jp-a)" transform="translate(88 -32)">
<path fill="#fff" d="M-128 32h720v480h-720z"/>
<circle cx="523.1" cy="344.1" r="194.9" fill="#bc002d" transform="translate(-168.4 8.6)scale(.76554)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 470 B

+24
View File
@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-kr" viewBox="0 0 640 480">
<defs>
<clipPath id="kr-a">
<path fill-opacity=".7" d="M-95.8-.4h682.7v512H-95.8z"/>
</clipPath>
</defs>
<g fill-rule="evenodd" clip-path="url(#kr-a)" transform="translate(89.8 .4)scale(.9375)">
<path fill="#fff" d="M-95.8-.4H587v512H-95.8Z"/>
<g transform="rotate(-56.3 361.6 -101.3)scale(10.66667)">
<g id="kr-c">
<path id="kr-b" fill="#000001" d="M-6-26H6v2H-6Zm0 3H6v2H-6Zm0 3H6v2H-6Z"/>
<use xlink:href="#kr-b" width="100%" height="100%" y="44"/>
</g>
<path stroke="#fff" d="M0 17v10"/>
<path fill="#cd2e3a" d="M0-12a12 12 0 0 1 0 24Z"/>
<path fill="#0047a0" d="M0-12a12 12 0 0 0 0 24A6 6 0 0 0 0 0Z"/>
<circle cy="-6" r="6" fill="#cd2e3a"/>
</g>
<g transform="rotate(-123.7 191.2 62.2)scale(10.66667)">
<use xlink:href="#kr-c" width="100%" height="100%"/>
<path stroke="#fff" d="M0-23.5v3M0 17v3.5m0 3v3"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+382
View File
@@ -0,0 +1,382 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-mx" viewBox="0 0 640 480">
<defs>
<radialGradient xlink:href="#mx-a" id="mx-b" cx="842.3" cy="103.7" r="25.9" gradientTransform="matrix(.14152 .03595 -.03453 .14198 213.1 162.4)" gradientUnits="userSpaceOnUse"/>
<radialGradient xlink:href="#mx-a" id="mx-c" cx="651.5" cy="550.5" r="25.9" gradientTransform="matrix(-.13441 -.05384 .04964 -.12489 397.9 -24.3)" gradientUnits="userSpaceOnUse"/>
<radialGradient xlink:href="#mx-a" id="mx-d" cx="380.8" cy="740.4" r="25.9" gradientTransform="matrix(.07536 .00282 -.00343 .14804 412.4 -203.6)" gradientUnits="userSpaceOnUse"/>
<linearGradient id="mx-a">
<stop offset="0" stop-color="#fff"/>
<stop offset="1" stop-color="#f15770"/>
</linearGradient>
</defs>
<path fill="#ce1126" d="M426.7 0H640v480H426.7z"/>
<path fill="#fff" d="M213.3 0h213.4v480H213.3z"/>
<path fill="#006847" d="M0 0h213.3v480H0z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m355.8 289.4.2 4.5 1.7-1.1-1.3-3.7z"/>
<circle cx="355.6" cy="288.2" r="1.4" fill="#fcca3e" stroke="#aa8c30" stroke-width=".2"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m361.1 296.4-3.2-3.1-1.5 1.2 4.5 2.6z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M360.9 298.2q-.7-1 .3-2 1.2-.7 2 .2.7 1-.3 2-1.2.7-2-.2z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m386.3 249.6 3.4 3.3.4-1.7-3.1-2z"/>
<circle cx="385.9" cy="248.7" r="1.4" fill="#fcca3e" stroke="#aa8c30" stroke-width=".2"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="M395.2 251.6 390 253l.5-1.7 4.4-.4z"/>
<circle cx="396" cy="250.8" r="1.4" fill="#fcca3e" stroke="#aa8c30" stroke-width=".2"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m378 276.8-3.2-4.8.5-.3 3.5 4.2z"/>
<circle cx="374.5" cy="270.8" r="1.4" fill="#fcca3e" stroke="#aa8c30" stroke-width=".2"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m378.1 277 4 .7.1-.5-3.3-1.4z"/>
<circle cx="383.3" cy="277.7" r="1.4" fill="#fcca3e" stroke="#aa8c30" stroke-width=".2"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M284.6 288q-.1 1.2-1 1.2-1.2 0-1-1.2c.2-1.2.5-1.3 1-1.3s1 .7 1 1.4z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m284.6 290.3 1 5-1.3-.5-.4-4.3z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M285.7 288.6c.6.7-.4 1.9-1.4 2.2s-2.4-.2-2.4-1.2 1.6-.5 2-.6c.6-.2 1.2-1.2 1.8-.4z"/>
<ellipse cx="277" cy="296.3" fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" rx="1.6" ry="1.1"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m279.6 296 4.8-.2-.8-1-4 .4z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M280 295.4c.5 1.3.5 3-.9 2.7-1.4-.1-1-1.4-1.2-1.8-.2-.9-1-1.7-.2-2.5s2 .4 2.3 1.6z"/>
<ellipse cx="264.4" cy="269.2" fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" rx=".9" ry="1.4"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m264.4 272.4.1 4.6-1.2-1v-3.8z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M266.2 271c.3 1-1.3 1.6-2.4 1.4s-1.9-.7-1.7-1.7 1.5-.8 2-.5 1.8-.8 2.1.7z"/>
<ellipse cx="256.2" cy="276.5" fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" rx="1.6" ry=".7"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m259.1 276.5 3.6-.3 1.6 1.2-5.3-.2z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M257.8 274.5c1 0 1.6 1.1 1.5 2.3q-.4 1.8-2 2c-.9-.2-.8-1-.8-1.2 0-.3.5-.7.6-1s-.3-1.3-.1-1.6q0-.5.8-.5zm-3-28.3c-.4.6-1.2 1.1-1.6.9s-.2-1.2.2-1.8q.7-1 1.4-.8.6.6 0 1.7z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m250.7 253.5 2-4.8-.2-.3-2.4 3.4z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M252.4 248.7q-1.4-1-1-2.3c.4-.7 1.2-.2 1.2-.2l.8.7c.4.2 1 0 1.4.6.5.6.2 1.2-.1 1.4s-1.5.4-2.3-.2zm-8.5-.6q1.1.7.9 1.5-.5.7-1.8 0-1-.6-.8-1.5.5-.7 1.7 0z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m246 250.2 3.8 2.2-.1 1.8-4.1-3.5z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M246 250.6q-1.2 1.3-2.3.7c-.7-.5-.1-1.2-.1-1.2l.8-.7c.2-.4 0-1 .7-1.4s1.2 0 1.3.3c.1.4.3 1.5-.4 2.3z"/>
<path fill="#aa8c30" d="m356.6 289.8-.4.4-.3 1v-1.5l.5-.3zm.2-.9s-.3.5-1.1.5-1.2-.4-1.3-.8q-.2-.5.2-1.2c.2-.5-.4.4-.4.4v1l.6.6.5.2h.4l.7-.2.4-.3zm4.2 7.3s-.4.2-.7.1c-1.9-.6-3.5-1.8-3.5-1.8l3.8 2.4zm2.4 1.1s0 .7-.8 1q-1 .4-1.5-.1c-.5-.5-.4-.6-.4-1.2l-.1.6.4.8.8.4.5-.1.5-.2.4-.5.3-.5zm24.3-47.6-.1.4v.7l-.9-1 .4-.5zm-.5-1.2c0 .7-.9 1.6-1.6 1.3q-1.2-.5-.9-1.8l-.2.3.1 1.1.7.6h.9l.6-.3.4-.7zm10 1.8s-.1-.4 0 0-.6 1.7-1.4 1.6q-1-.3-1-1.4c0-.6-.1.2-.1.2v.8l.5.4.6.4.6-.2.7-.3.2-.6v-.9z"/>
<path fill="#aa8c30" d="M393.8 251s0 .4-.4.7l-1.3.7 2.8-.8-.2-.7zm-17.1 22.5-.1.5v.8l-1.6-2.5.5-.3zm-.9-2.4c-.2.7-1.4 1-2 .8-.5-.4-1-1.6-.3-2l-.4.2-.1.7.2.7.6.7.5.1.8-.2.6-.4s.4-1.3.1-.6m5.6 5.7s0 .3-.5.3h-1.6l2.6.6.2-.6z"/>
<path fill="#aa8c30" d="M384.6 277.5c.3.8-.5 1.7-1.5 1.4s-1.3-1.3-1-1.8l-.2.6.2.8.6.6h1.3l.6-.7.2-.7-.1-.2zm-99 11.1c.3.3-.1 1.5-1.2 2q-1.7.4-2-.4-.4-.7-.3-.8l-.2.2.2.6.5.5 1 .2.8-.1.7-.5.4-.3.3-.5v-.6z"/>
<path fill="#aa8c30" d="m284.6 291.1-.3.4-.3.8v-1.4l.5-.3zm-3.8 4s0 .3.2.5l1 .3h-1.8l-.2-.7h.8zm-.6 1.2v.2l-.1.5q-.1.9-1 .9a1 1 0 0 1-1-1.1q-.1-.6-.2-.6v.6l.4 1 .7.3h.5l.5-.2.2-.6.1-.5zm-4.7-.1s.2.8 1.2.8 1.2-.2 1.2-.2v.3l-.2.2H276l-.3-.3-.3-.5v-.3zm-9.4-25.4v.4c-.1.3-.7 1-1.8 1q-1.4 0-1.8-.7t-.4-.8v.5l.4.7 1.1.5h1l1-.3.5-.5.1-.6zm-1.7 2.3s-.4 0-.6.2l-.4.4v-1.3h1zm-4.7 3.4.5.3c.5.3 3.1.5 3.1.5h-4.1l.1-.8zm-.5 0-.2.6c-.2.5-.7 1.6-1.6 1.4-.9-.1-.7-.8-.7-.8v-.4l-.2.7.2.4.4.2.7.1.4-.2.6-.5.2-.5.2-.6zm-2.2.2s0 .2-.7.2-1.7-.3-1.7-.3l.4.4.5.2h1.3zm-2-29s0 .5-.3.8c-.4.3-1.3.5-1.8.2s-1.5-1.3-1.5-1.3l.4.8.7.6 1 .3 1-.2.4-.4.2-.3z"/>
<path fill="#aa8c30" d="M252 249.1v.6l-.2 1.1.9-2-.4-.2zm-5.4 1.5.2.5c.2.5 2.3 2.6 2.3 2.6l-3.3-2.9.4-.5zm-1 .3s-.5.3-1 .3c-.3 0-1 0-1-.4q0-.7.1-.7l-.3.6.1.4.5.3h.7l.6-.3.2-.2zm-1.5-1.4h-.5c-.7 0-1.4-.8-1.4-.8l.5.8 1 .5z"/>
<path fill="#9ca168" d="M399.9 240.2c-.3 3.8-4.1 5.8-6.1 7.2s-3 3.2-3 3.2l-.6 2.2-.3 1.5-.1.5q.2.6.3 2l-.1 4.3 2.7-1.9 1.9-.7.6-.1s-2.2 2.1-3 4.4-2.4 7.2-5.3 8.8c-3 1.6-4.8 1-5.7 1.8s-1 1-1 1l-1.3 1.8-1 1.2-.9.7-.6.4-.2 1.7-.7 2.4s.6-.4 1.3-.5h1.3s-.5.6-.7 1.3c-.2.6.1 4.6-3.6 7-3.8 2.3-13.4 2-13.4 2l-1.8.7-1.7 1.3-1.6 1.7v.4s-1.3 1.5-2.1 2l-2.8 1.9 2.2.1 3.5 1.5s-2.2 0-3.6.5c-1.5.6-8.2 4.4-11.4 4.3s-8-4.9-8-4.9l-2-1.4-3.5-.7-4.3-.2v-.6l.1-.7s1.5-.2 3.8.1c2 .2 2.7 1 4.4 1.1q2.6.2 3.5-.3c.7-.3 5.9-4.7 5.9-4.7l5.8-2 2.3.3 1 .2.9.3-1 1-1.4 1.1.7.6 3.7-.6 1.2.3.3.2q0-.5.6-1.3c.4-.6 2.5-2.3 3.4-2.9l1.4-1c.3-.3 1.5-3.3 1.5-3.3l.1-1.6 4.1-4 2.8-2.9 1.2-3.3-.2-.7s1 1 .9 3c-.2 2.2-.7 2.9-.7 2.9s2.8-2 4.3-2.6q1.8-.6 2.4-.5c.4-.2 1.5-.9 2.1-1.8.8-1.2 1-1.5 1-2l.2-2.8-.3-7 2.4-5.4 3.5-3.1.7-.4-.4 1.2v1.5s1.8-2.5 2.7-2.9l.5-.3.8-2 .3-3v-3l-.5-2.3-1.7-4.3v-5.8l-1.2-1.6s1.1 0 2.5 2a14 14 0 0 1 1.9 5l3.3-10.3s.9 1.2 1.5 3.3l.8 3.3 1.4-2.8.1 1c.2 1 2.3 2.1 2 6m-67.3 65s-.7-1.6-3.9-3.2c-3.1-1.5-5.5-1.8-5.5-1.8v1.1l5 2.2 2.4 2z"/>
<path fill="#717732" d="M355.4 295.6c.3-.4 3-2.8 4.4-3.5s3.2-.8 3.2-.8c1.9-.4 2.2-.1 6.6-2.5s5.3-5.2 6.2-5.9c.8-.7 2.3-.8 2.3-.8l-3.4 3.9a29 29 0 0 1-5.8 4.5 18 18 0 0 1-7.6 1.8 8 8 0 0 0-4 2c-1.3 1-2 2.2-2 2.2s-.2-.5 0-.9zm12-12.7c-4 3.7-5.3 7.2-5.3 7.2l5.4-4.9c1.9-1.8 4.5-2.6 5.6-3.7 1.2-1.1 1.3-2 2-2.7l1.3-1s-1.8-1.4-9 5.1m-8.5 8.6s1.7-.8 2.4-2.3c.5-1.2.2-1.5 1-2.8 0 0 4.2-4 5.7-5.8 2.6-3.1 1.4-5.7 1.4-5.7s.3 1.3-.8 2.7c-1 1.4-6.5 5-7.2 7.2-.6 2.2-.3 2.3-.6 3.4-.6 2.6-1.9 3.3-1.9 3.3m-2.8 10.5s-2.1-1.4-4.5-1.3c-4.9.4-9 3.4-12.2 3.4-3.1.1-4.4-1.5-6.9-3.2-2.6-1.8-9.2-1.5-9.2-1.5v.3s4.4 0 6.4.6c3.6 1 5.6 4.8 9.7 4.6 5.5-.3 9.9-3.6 12-3.6 3.8 0 4.7.7 4.7.7m-24-2.4s3.7.6 6.6-1.5c3-2.1 6.4-5 8.3-5.2 2 0 4 .4 4 .4s-2.5-1-4.4-1q-3-.2-5.8 1.3c-1.8 1.1-2.7 3-4.6 4.3a13 13 0 0 1-4.1 1.7m-.7 5q-.9.1-1.3.3c-.2 0-1.3-1.2-3-2-1.6-1-4-1.4-4-1.4s-.3 0 .3.2l3.8 1.5a9 9 0 0 1 2.6 2.1c.1.3.6.9 1.5.8q1.4-.1 1.2-1-.2-.5-1.1-.5m2.6-3.6s1.5.8 4 .8c5.2-.2 8.6-3.8 12-5 3.4-1.4 5.2-.3 5.2-.3s.1-.2 0-.2a7 7 0 0 0-3.9-1.2c-5.7 0-11.2 3.7-13.5 4.7q-3.6 1.3-3.8 1.2m56-39.9c-1.8 1.9-4.5 7.7-5.6 9.3-1.2 1.6-2.7 2-3.3 2.7-.6.6-2.7 3.5-3.2 4.1-.5.7-.6.5-1 .8q-.6.4.1.2c.6-.1.8-.5 1.6-1.5 1-1 .9-1.4 2.3-2.7 1.4-1.2 3.9-2.6 5.1-4.2 1.3-1.5 3.8-7.5 5.2-9s4-2.4 4-2.4-2-.6-5.3 2.7zm-9 11.1s-.1-1 1.2-2.6 1.7-1.3 2.8-3.6q1.3-3.4 2.3-6.9c.9-2.4 2.3-4.8 2.3-4.8s-1.3.2-2.7 2a35 35 0 0 0-4.8 8.7c-1.1 3.4-1.1 7.2-1.1 7.2m-1.7 2.2s.5-.2.5-3.2c.1-2.9-.1-7 .7-9 .9-2 5.4-7 5.4-7s-1.9.5-4.5 2.5-3.7 4.7-3.6 6.7 1.3 4.2 1.4 6.1l.1 4zm10.5-20.1s1.1-4 1.6-5 .5-1.5 2.7-4c1.3-1.5 2.6-2.4 3.1-4.4s.6-7.7.6-7.7-.6.5-1.1 1.5-.1 4.7-1 6.4-2.7 5.6-3.7 6.5c0 0-.2-2.9.2-5.7.3-3 1.3-3.8 1.7-5.7s.2-6.7.2-6.7-1.5 1.5-2.3 3.4-1.2 5-1.2 7.5.5 4.5.6 5.9.2 2-.4 3.6l-.6 2-.7 2.6zm-3.5-21.6s1.4 1.7 1.2 3.6c-.3 2-1 4.5-.2 6.5.7 2 1.9 2.2 2.2 3.4s.3 3.4.3 3.4.6-4.3.2-5.5c-.5-1.1-.7-.6-1.3-1.8s0-4.7-.4-6.7-2-2.9-2-2.9"/>
<path fill="#9ca168" d="M306.7 304.8s.4-1 3.1-2.4a34 34 0 0 1 7.3-3c.3.3-.3 1.8-.3 1.8l-2.6.8-2.1 1.2-2.5 1.6z"/>
<path fill="#9ca168" d="M313.6 297.7c-3.4.1-5.3 1-6 1-.2.1-.7.5-1.2.3a5 5 0 0 1-1.5-1.5l-.7-.7-.2 2.5-5-4.4-.6 3-.7 1.3-5-4.8.2 3.7-1.4.1-3.6-2.5-1 .4 1.2 2.2-4.6.3-1 .8-1 1v.4h1.9c.4-.2.6-.7.7-.5.2.2.3 1.2.9 1.1s2.7-1.7 4.2-1c1.7.8-2.2 2-1.8 3.1s3.2.7 4 .3c.6-.3 2.7-3 3.6-2 1.2 1.4-2.5 2.5-1.8 3.9s2.7 1 3.7.4 3.4-4.1 3.9-3.4c.9 1.4-2 2.7-1.3 3.7.8 1 2.3 0 3.4-.8s1.5-2.5 3-3.3c1.4-.8 1.3-.5 1.7-.5s1.5-1.1 1.5-1.1l3.5-1.5 1.6.2.9.4 1.7.2.2-1.4s-2-1-3.3-1zm-27.4 0 .7-1.3-.4-.6s-1.2-.7-2.3-1.8a7 7 0 0 0-2-1.6L280 291l-.8-2.8-.5-.9-1.3.6-1.7-6.1-.3-1.1h-.7l-1.5 3.6-1.5-2.3-.4-5-1 1-1.3 1.4-2.3-4.6s-.1-.1-.3.3c0 .3-.3 1.8-.1 2.6a26 26 0 0 1-6-5.2l-1-2.5 1-2 .5-2.6-2.2.9-.4-5.7-.3-1.8-2.7 3.8-1-1.7v-3.6l-.7-.2-1 2s-1.1-2-1.6-2.5c.1-.8.2-2.9-.3-4.5-.6-2-1.3-3.1-1-4.8.2-1.6.9-1.8.8-2.5 0-.8-1.2.5-.8-.5s3.8-3.2 3.1-4.4c-.7-1.1-4 1.9-3.3-.1s4-2 4.2-4.9c.1-1.7-3 1.1-3.3.2-.3-.8 2-2.6 1.9-3.8 0-1 .4-1.2-.2-1.8s-2.6 2.3-2.6 2.3l-2-.7-.8 3-.6 2.5-2.6-1.5.7 3.1.6 3-2.7-.7 1.4 2.6 1.9 2 1.1 1.5.9.4 1 1.1.5 1.7.6 1.9.1 1.8v2.4l-.1.4v1.1c-.5 0-1.4-.8-1.7-.3s1.6 2 1.2 2.4c-.3.4-3-.5-3.2.3-.2 1 .2 2.2 1.8 2.6s5.3 1.2 4.7 2c-.6.7-4.7-2.3-4.5-.1a4 4 0 0 0 2.6 3.4c1 .4 5.1 0 4.9 1-.3.9-3.3-.4-3.6 1s2.1 1.6 2.6 1.6 2.2-.1 2.9.4l4.3 4.1 4.6 3.4c-.8 0-2.4-.3-2.6.3s6 3 4.2 3.8-3.8-2-4.3-.4 1.2 3 2.2 3.6 6.7.1 5.6 1.4-5.3-.8-5.3.7 2.7 4 4.2 3.9c1.4 0 3-2.4 3.6-1.1s-1 1.7.3 2 2.3-1.4 4-1c1.5.3 4 1.3 5.4 2.5z"/>
<path fill="#717732" d="M308.4 304.1c1 0 1 .6 1.2.6s1.8-1.5 3.3-2.3a18 18 0 0 1 4-1.4l.1.2s-2.8.7-4.2 1.7l-3 2.1q-.3.5-1.7.7c-1 0-1.4-.6-1.4-.9 0-.2.7-.7 1.7-.7m8.5-4.3s-.9.1-1.3-.3a4 4 0 0 0-2.6-.8 7 7 0 0 0-4 1.8q.1.2-1.4 1.3.6 0 2-1a7 7 0 0 1 3.6-1.4c1-.2 1.7.4 2.2.7.5.4 1.4.3 1.4.3zm-50.5-20.6v-.7l-2.2-1.1c-1.6-1-5-3.8-5-3.8l2.8 2.7c1.4 1.3 4 2.9 4 2.9zm35.6 25.2 1.5-2.1q1.5-2 1.9-2.2 0-.5-.7-1.4l-.2-1.2s.4.8 1 1.3l1.1 1s1.4-.2 1.4-.5q0-.3-.2-.5c-.3-.1-.5.2-1.1 0-1.3-.6-1.8-2.6-2.6-2.6-.8-.1-.2 2-.6 2-1.1.2-2-4.3-5-4.5-2 0-2.3.3-2.4.7 0 .5 2.5 3.1 1.6 3.5s-3.5-4.2-5.5-4.2c-1.9 0-2 .7-2 1.1.2.4 2.2.8 1.8 2.2s-2.5-2-4.3-1.9-2 .3-2 .9c-.1.5.6 1.2.3 1.4s-1.4.1-2.3.7-2.1 2.4-2.1 2.4 1.2-1.7 2.7-1.9h5.6l-1.1-.8c-.6-.5-1.1-1.6-1.1-1.6l1.7 1.5c.8.7 1.9 1.2 1.9 1.2s1.7.2 1.8.4q.3.3-.8 1.3l-1.7 1.7 2-1.6 1.5-1.2 1.4.2q.3 0-.8-1.7l-1.6-2.5s1 .9 2 2.3c1.1 1.3 1 2 1.4 2q.7.1 1.4.1t-.4 1.7q-1.4 2.5-1.2 2.5c.2 0 .8-1.2 1.6-2 .7-.8 1.1-1.7 1.5-1.8h1.5l-.8-1.6c-.6-1-.8-2.6-.8-2.6s.6 1.4 1.4 2.5l1.3 1.9 1.4-.1.3.1c0 .5-.2.7-.7 1.8zM280.5 292q1.7.6 1.7.5c0-.1-1.7-1-2.1-2.4s0-4.4-.8-4.3c-.7 0-1.1 2.1-1.8 1.7-.8-.4.2-4.4-.6-6-.8-1.7-2.6-2.9-3-2.3s-.3 4.2-1.5 3.5.1-4-.2-5.3c-.4-1.5-1.2-2.5-1.9-2.1s.3 3-.7 3-.9-1.9-1.3-2-.6.3-.9 0c-.2-.4 0-1.7-.6-1.6s-.5 1.2-.3 1.7q.3 1 .7 1.8c.2.4 1 .9 1 1.2q-.2.5-1 .6h-1.9s1.4.4 1.9.4 1.4-.2 1.7.2c.2.5 1.4 2 1.4 2s.3-.8.4-2.2 0-2.7 0-2.7.5 1.6.4 2.7c0 1-.4 3-.4 3s1.1.8.8 1c-.3.3-1.5.3-2.8.3-1.4 0-3.3-.5-3.3-.5a14 14 0 0 0 6.7 1.3l1.8 2s.9-1.5 1-3v-3l.4 2.8c0 1.3-.4 3.6-.6 3.8a5 5 0 0 1-1.9 1l-3.5.5s2.2.3 3.8 0c1.5-.4 1.7-.9 2.2-.6l.8.7 1.3 1c.1.1-.7.4-1 .7l-1.5.7 2.2-.7 1.1-.4.5.2-.1-1.2-.3-1.8s.5.8.7 1.6l.1 1.6s.2.2 1.4.6m-31.1-35.6s.3-1.7.3-3.6a14 14 0 0 0-1.9-5.8l1-1.4-1.2 1-1.2-.5-.9-1 1.2.8q.8.2.7.1l-.5-2-1.9-1c-1.3-.6-2.5-1.8-2.5-1.8l2.8 1.5q1.5.5 1.6.4l1.6-.6 1.7-1.1s-1 .6-1.8.7l-1.6.3-.3-2.4-1.1-1.1c-.9-1.2-1.6-2.9-1.6-2.9s1 1.2 1.7 1.7 1.4 1.5 1.4 1.5l1.6-1 2.8-2-2.7 1.3-1.6.7s-.2-1 0-1.5.8-1.2.7-1.6c0-.5-.4-.3-.5-.7-.2-.3.4-2.6.4-2.6l.1 1.2c0 .5-.2 1 .4 1s3.2-2.7 3.6-3.2.8-1.8-.6-1.3-1 1.8-2.2 1.7c-.4 0-.8-1.5-1.2-1.2s-1.4 1.3-1.6 2.4.2 2.6-.4 3.1-1-1.8-2.2-1.5-1.5 2-1.3 2.6 2.7 3.5 2 4c-.9.3-2.9-2.6-4-.6-1 1.9 3.4 4 4 4.4.5.5 0 .6.8 1.6s1.8 1.2 2.4 1.8a13 13 0 0 1 2.1 5.9zm11.7 17.1s-1-1.2-.7-3.3 1.8-5.7 1.3-6.2-2 2.2-2.4 1.6.9-4.3.2-5.8-.7-2.5-2-2.3c-1.3.1-1.8 4.4-2.5 3.6-.6-.7.4-2.6 0-3.7q-.6-1.4-1.3-1.2c-.7.2-1 2.2-1.4 2.1-.5 0-1.2-2.3-2.2-2-.9.3 2.3 4.5 2.3 4.5s.5-.5.8-1.2l.6-1.5s.3 1.3 0 1.9-.3 1.7-.3 1.7-.8.3-2.1 0l-2.3-.7s.8.6 2.1 1l2.7.7 2 2.7.8-2.1 1.3-3.5-.6 3.9-.7 3s-1 0-2.6-.3l-3.6-.8 3.6 1.2 2.8.7 1.4 1.9 1-1.5 1-1.8s-.3 1.4-.7 2.3l-.7 1.4-1.8.2h-2.5a20 20 0 0 0 4.5.9s0 .5.7 1.3z"/>
<path fill="#fff" d="M314.4 310.8s-.6-.6-1.4-1q-1.3-.6-1.2-.4l-.4-.1.8-2 5.5-5.3.9-5.3h3.4v7.2l1 .5 6.2 3.7v1.6l-1 .4-.7.4-2.3.2-4-3-1.8-1.7-3 4z"/>
<path fill="#016848" d="m318.8 296.7-.1 1c0 .9 0 5.2-.2 5.8s-3.2 1.8-4.9 3.3-1.8 2.6-1.8 2.6-.5 0-1.6.4c-1 .4-1.5 1-1.5 1s.6-2.6 3.4-5c2.7-2.6 4-2.8 4.4-3.2.3-.4 0-5 0-5.5q.3-.5.7-.4zm7 15.7.8-1.1 1-.8-1.5-.8c-1.5-.6-2.3-.4-3.3-1.2l-2.2-2-1.5 1.2 1.7 2c.9.8 2.7 1.1 3.5 1.6l1.6 1.1z"/>
<path fill="#cd202a" d="M321.5 296.6s1.7 0 1.9.2l.2.6c0 .1 0 4-.2 5.3a8 8 0 0 1-1.4 3l-4 3.7c-1.2 1.2-2.4 2.8-2.4 2.8l-.8-1q-.6-.5-.6-.9a10 10 0 0 1 3.3-3.4c2-1.3 3.6-2.8 4-4.9.3-2 0-5.4 0-5.4"/>
<path fill="#cd202a" d="M332.5 310s-.4-.4-1.8-.4l-1.5.1s-.8-1-2-1.7c-1.2-.8-2.1-.7-3.5-1.6-1.3-.8-2-2.1-2-2.1l1-2s1.2 1.5 2.4 2.4 3.7 1.7 4.6 2.4z"/>
<path fill="#30c2dc" stroke="#0872a7" stroke-width=".5" d="M345 286.4s-3.6-1.4-3.4-2.7c.3-1.2 8.3-3.3 8.3-3.3l.1-2.1s-1.2-.1-3.2.5-5.2 1.6-8.6 1.6l-34.4-2.2c-3.7-.3-6-6-6-6l-1.8.4s.5 2.7-.4 3a65 65 0 0 1-13.9-6l-.6 2.6s7.3 4 7.1 5.5c-.2 1.4-2.4 1.3-2.4 1.3l1 1.8c.3 0 12 .6 12.3 4.1 0 1.5-3 2.3-3 2.3l1 1.2v.6s6.7 0 8.5 1 2.7 2.6 5 3.8c2.2 1.3 17.3 1 19.6.3 2.7-.7 4.3-3.7 8.7-5 4.3-1.4 5.7-1.4 5.7-1.4z"/>
<circle cx="284.5" cy="280.7" r="2.1" fill="#fff" stroke="#0872a7"/>
<circle cx="296.4" cy="270.9" r="2.1" fill="#fff" stroke="#0872a7"/>
<circle cx="346.5" cy="286.6" r="2.1" fill="#fff" stroke="#0872a7"/>
<path fill="#f8c83c" d="M275.1 267.8c-.7 1.6 2 4.6 4 5q2.6.3 3.2-1.1a3 3 0 0 0-.4-2.5c-1.2-1.7-6-3-6.8-1.4"/>
<path fill="#fff" d="M281 270.2c0-1-1.6-1.9-2.6-1.9s-2 0-1.8.5 2.7 1.3 2.8 1.6-.8.6-.4 1q.8.4 1.4-.2.6-.3.7-1z"/>
<path fill="#f8c83c" d="M297.7 288.3c.4 1.4-.7 2-2 2.7-1.5.6-4.3.2-5-.9-.6-1.1 1-3 2.8-3.4s3.8.3 4.2 1.6"/>
<path fill="#fff" d="M294.8 289c.5 0 .6 1 1 .8.5 0 1-.8.8-1.3s-1-1.3-1.8-1.2c-.8 0-3 2-2.8 2.5q.4.7 1.2.3c.4-.2 1-1 1.6-1z"/>
<path fill="#f8c83c" d="M349.2 281c1.3 1.7 3.6.2 4.2-.5s2.4-1.7 1.7-2.8c-.8-1.1-2-1-3-1-.8 0-2.6 1.4-2.9 2-.3.5-.6 1.6 0 2.4z"/>
<path fill="#fff" d="M349.6 280s.1-1.9 1.8-2c1 0 1 .3 1.8.7.7.4 1-.5 1-.5s0 1.3-1.1 1.3-.8-.5-2-.7c-1-.2-1.5 1.1-1.5 1.1z"/>
<path fill="#f9aa51" stroke="#953220" stroke-linecap="round" stroke-linejoin="round" stroke-width=".5" d="m321.9 276.4-.9-.6h-2.8l-3.4.1 3.4 9.9 4 5.8 1.6.6 3.1-.2.6-1.6-1.2-9.5z"/>
<path fill="#f9aa51" stroke="#953220" stroke-linecap="round" stroke-linejoin="round" stroke-width=".5" d="m310.6 277 .2-1.1 1.8-.1 2.2.1s2.3 2.9 3.1 4.7 1.7 4.8 2.7 6.6c1 1.9 3.2 5 3.2 5h-4l-2-.6-5.6-9z"/>
<path fill="#f9aa51" stroke="#953220" stroke-linecap="round" stroke-linejoin="round" stroke-width=".5" d="M308.2 275.8h2.6a14 14 0 0 1 3.9 5.4c1 2.8.5 3.2 1.8 6.2 1.4 2.9 3.2 4.9 3.2 4.9s-3.9.2-6-.2c-2.3-.4-3-.3-3.6-1l-1.7-1.3h-2.2l.5-3.1-.6-6.9.1-3.5zm23.4 1.8-3.7-1.5-7-.4.6 3.6a30 30 0 0 0 2.6 7.7c1.1 2 2.2 4.4 2.8 5s4.6-1.1 4.6-1.1l2.6-.4-.3-2.8-.4-1 .6-8-.6-1.2z"/>
<path fill="#f9aa51" stroke="#953220" stroke-linecap="round" stroke-linejoin="round" stroke-width=".5" d="M307.1 277.7c0 .8 1 .9 1.2.8s1-.3 1-1.5-.7-2.1-2.2-2.2c-1.4 0-2.5 1.7-2.5 3 0 1.2 1.2 1.8 1.2 2.3 0 0-1.2 1.1-1.1 3.1s1.6 3.6 1.6 3.6-1.7 1.3-1.7 2.7 1.3 2.3 2.6 2.3c1.2 0 2.8-.8 2.8-1.8s-1-1.7-1.7-1.7q-1.1.2-1.1.8m25.6-10.9c0 .9-.6 1-1 1s-1.2-.4-1.2-1.5c0-1 1.4-1.6 2.4-1.6s2.4 1.2 2.4 2.7-1 2.6-1 2.6.6.3.6 2.2-1.2 3.3-1.2 3.3 1.6.6 1.6 2.6-1.4 2.6-2.3 2.6c-1 0-2.6-.5-2.6-1.9s.8-1.8 1.5-1.8q1.1.2 1.3 1.5"/>
<path fill="#953220" d="m309.5 288.6.4.6q.2.7.8 1.4c.8.7 7.2 1 9.2 1s7.8.2 8.9-.4c1-.7 1.2-1.8 1.9-2.3l.9-.4-.9.7v1.4l.5.8s-.1.4-.8.8q-1 .5-2.3.6c-1 .1-13.2.1-15.2-.2s-1.8-.3-2.4-.7l-1-1 .3-.8-.3-1.4zm20-11.8 1 .5s-.9.3-1.6 2.3-.1 2.5-.6 2.7-5.8 0-5.8 0l-1-2.1z"/>
<path fill="#231f20" d="M346.4 276s-.9-.3-1.3-1q-.8-1.4-.5-2c.4-.1 1 .5 1 1.1s.8 2 .8 2zm10.4-2.8s.7 1.2 1 1.3l1.2.6s-1.5 0-2-.4l-1-1.5z"/>
<path fill="#231f20" d="M360 274.2s-1 .5-1.6.2q-1.4-.5-1-1t.8.2c.4.4 1.8.6 1.8.6m5.3-4.5s-.8.6-1.3.6-1.5-.2-1.5-.4 1.3-.4 1.6-.3zm-8-10.5s-1.1.4-1.5 1-.3 1.2.2 1.2.6-.7.6-1zm-5.2 2.9s-.9.5-1 1c-.3.5-.5 1.6 0 1.6s.7-.8.7-1.2c0-.5.3-1.4.3-1.4"/>
<path fill="#8cbebf" stroke="#04534e" stroke-width=".5" d="m342.8 268.4-2.9 3s6.6 3.7 11.7 3.5c5-.1 10.8-4 11.2-5 .4-.7 0-4.3-.6-5.4s-4.2-3.8-5.5-3.7-3.7 1.9-5.8 4c-2.2 2.1-2 3.9-5 3.7z"/>
<path fill="#0c8489" d="M342 269.6s7 1 9.1-.8 4.3-5.3 5.7-6c1.4-.9 2-.9 2-.9l1.6 1.1 1.8 1.5.6 3.4v1.8l-2.2 1.8-4 1.9-4 1.2-3.2-.1-6.7-2-1.7-1.1-.4-.4z"/>
<path fill="#04534e" d="M352.8 265.5q0-.7 1-.6c1 .1 1 .2 1 .8s-.5 1.4-1 1.4-1-1-1-1.6m2.8 1.8q0-.9 1-.8c.7 0 1.3.7 1.3 1.2s-.5 1-1.1 1-1.2-.9-1.2-1.4m-1 4.2q.1-1 1.1-.8 1 0 1 1a1.2 1.2 0 0 1-1.1 1.2q-1-.2-1-1.4m-3.7-1.3c0-.7.7-1 1.3-1q1 .2 1 1.4c0 1.2-.4 1.3-1 1.3s-1.3-1-1.3-1.7m-5.9.7a1.4 1.4 0 1 1 3 0 1.4 1.4 0 0 1-3 0"/>
<path fill="#8cbebf" d="M355.6 267.3c0-.5.7-.8 1-.8q.8.1.8.8 0 1-.7 1-.8-.1-1-1zm-1 4q.2-.8 1.2-.8c1 0 1 .3 1 .8s-.5 1-1 1q-1 0-1.1-1zm-3.7-1.2c0-.6 1-.9 1.5-.9q.4 0 .3 1 0 1.1-.7 1.2c-.7.1-1-.7-1-1.3zm1.8-4.7q.1-.6.8-.5c.7.1.6.1.6.7s-.3 1-.6 1q-.7-.2-.8-1.2m-7.7 5.1q.1-1.1 1.4-1.3 1.8-.2 1.7 1c0 .6-1 1.8-1.8 1.8s-1.3-.8-1.3-1.5"/>
<path fill="#231f20" d="M347.7 269.4s-1 .1-1.5.6-.5 1-.3 1.2.7-.3.8-.6c0-.2 1-1.2 1-1.2m4.7-1s-1 .6-1 1.3q-.1 1 .2 1c.3 0 .5-.9.4-1.2s.4-1.2.4-1.2z"/>
<path fill="#231f20" d="M353.2 269.3s-.6 0-1 .4q-.9.9-.6 1 .6 0 .8-.5c0-.2.8-.9.8-.9m1-5.4-.8.6c-.2.2-.5 1-.2 1q.4.3.8-.5.2-1 .2-1.1m3.5 2.3s-.9 0-1.2.5q-.6.7 0 .8.4 0 .6-.5c.1-.4.6-.8.6-.8m-.5 4.1s-.2.7-.8 1q-.7.6-1 .1 0-.6.4-.7z"/>
<path fill="#04534e" d="M362.8 267.5s.4 2-1.3 3-6.4 4.2-10.9 3.8-9.6-3-9.6-3l-.7.4 1.2.5 3.4 1.4 4 1.2 2.5.1 2.1-.2 4-1.3 3.3-1.7 1.8-1.4.4-.6v-1.9z"/>
<path fill="#231f20" d="M274.9 242.2s1 .9 1 1.6q-.3 1.2-.7 1c-.3-.2-.3-1-.3-1.3zm7.7 7.1s-.3 1.4-.9 1.5c-.5.1-.9-.4-.8-.7q.3-.1.8-.2zm-16.8 1s.4.8 1 1q1.1-.2 1-.6-.2-.2-.8-.2zm3 6s.7.6 1.5.7a1 1 0 0 0 1.3-.7c0-.2-1-.2-1.3 0s-1.5 0-1.5 0m5.3 4.4s1.4-.2 1.6-.6.6-1 .3-1.3-.5.5-.7.9c-.1.3-1.2 1-1.2 1"/>
<path fill="#8cbebf" stroke="#04534e" stroke-width=".5" d="M282.6 257.1s1-6-4-10.4c-5-4.3-8.1-3.2-9.8-1.6s-3.2 6.7 2 11.3c5.3 4.5 11.7 3 11.7 3z"/>
<path fill="#0c8489" d="M267.6 250.7s-.1-3.4 1.5-4.4c1.5-1 6.8-1.8 9.3 3.1s2.7 7.4 2 10h-2.7l-4.3-1.8-3.5-2.6z"/>
<path fill="#04534e" d="M282 258s-.9 1-2.7 1c-1.7-.2-10-2.3-12-9.8v1.7l.1.5.6 1.5 1.7 2.5 2.2 1.9 2.8 1.4 2 .6 2.5.4h1.5z"/>
<path fill="#04534e" d="M277.7 255.9q0-.9.8-.7c.8.2 1-.2 1 .4s-.4 1.6-1 1.6q-.7-.2-.8-1.3m.3-4q0-.7 1-.8.8 0 .9.9 0 .7-1 .8-.8-.1-.9-.8zm-3.7 4.4q0-.6 1-.7c1-.1 1 .3 1 .7s-1 .8-1.5.8-.5-.5-.5-.8m-.4-4.6c0-.6.3-.4.8-.4s1.2.1 1.2.7-.7 1.4-1.2 1.4-.8-1.2-.8-1.7m-3.4.7q0-1 .9-1.1c.9-.1 1.3.6 1.3 1.1q-.1.9-1.1 1-1-.1-1.1-1m.6-4q0-.7 1-.5 1 0 1.1.7c0 .5-.7 1.2-1.4 1.2s-.7-1-.7-1.5zm3.7-.5q.1-.5 1-.3 1 0 1.1 1c.1 1-.7 1-1.3 1s-.8-1.2-.8-1.6z"/>
<path fill="#8cbebf" d="M271 248.3q.2-.6.8-.7.8-.1.9.7-.1.8-.8.9-1-.1-.8-1zm-.6 4q0-.9.7-1 1-.2 1 .5-.1 1-1 1.1c-.9.1-.7-.1-.7-.6m4.4-4.3q0-.5.8-.4 1 0 .8.5-.1.7-.8.7-1-.1-.8-.8m-.9 3.9q.1.9 1 1 1.2 0 1-1c0-.6-.6-1-1-1s-1 .4-1 1m4.3-.6q0 .8 1 .9.8 0 .9-.9c.1-.9-.5-.9-1-.9s-1 .4-1 1zm-4.1 5q.2-.8.9-.9 1 0 .9.7c0 .4-.8 1-1.2 1q-.7 0-.6-.9zm3.6-.7q0-.7 1-.6.8 0 .8.6-.1 1-1 1c-.9 0-.8-.5-.8-1"/>
<path fill="#231f20" d="M278.7 249.4s.5.7.6 1.3v1.2q-.5 0-.7-.9c-.1-.5 0-1.6 0-1.6zm-3.2-3.3s.4.5.4 1 0 1-.3 1-.3-.4-.3-.8zm-3.7.5s.4.5.4 1 0 1-.3 1-.3-.4-.3-.8z"/>
<path fill="#231f20" d="m270.5 247.3 1 .4q.5.7.3 1-.3.2-.6-.4zm2.8 3.8s.7 0 1.1.5q.8.4.5.9-.4.1-1-.5zm-3.4 0 1 .3q.5.5.2.8-.2.2-.6-.4zm8.3 2.5.2 1.4q.1.9-.2 1c-.3.1-.3-.7-.3-1 0-.5.3-1.4.3-1.4"/>
<path fill="#231f20" d="m277.3 254.9.8.5q.4.4.1.6l-.4-.5zm-3.7.2.9.3c.2.2.3 1 .2 1.1q-.1.1-.6-.3z"/>
<path fill="#04534e" d="M272.1 243.8s-3.2.7-3.8 2.9c-.6 2-.7 4.4-.7 4.4l-.4-2.7.6-1.7 1-1.6 1.4-1 1.3-.4zm10.2 12.5s.4-.8-.6-3.4-3-6-3-6l1.6 1.8 1.2 2 .6 1.8.5 1.7v1.6l-.3.6z"/>
<path fill="#231f20" d="M285.6 252.2s1.2.7 1.3 1.1q.3.6-.1.7-.6 0-1-.7zm-4.8 12.8s.6-1 1.3-1.3q1-.6 1.4-.1.1.4-1 .9zm8.1 3.1s.8-.4 1.3-1q.7-.8.4-1-.5-.4-1.1.5c-.3.4-.6 1.5-.6 1.5m-.9-.7s.3-1 .6-1.4q.4-.5 1-.3.2.4-.2.9z"/>
<path fill="#8cbebf" stroke="#04534e" stroke-width=".5" d="M301.5 261.2s-5-7.2-12.8-7.6a7.4 7.4 0 0 0-8 7c.2 1.6 2.6 5.2 10.9 5.8s9.6-3.9 10-4.1c.3-.2-.1-1-.1-1z"/>
<path fill="#0c8489" d="M295.6 256.3s2 1.5 3 2.8q1.2 2 .5 2.3c-.5.2-5-4-8.8-4.9-3.7-.9-6.9 0-7.6 1.7s-.4 4.5 1.2 5.6c1.8 1 9.7 2.3 9.7 2.3l3.7-.5 2.3-1.3.5-1.3.6-1.6.2-.7-1.9-2z"/>
<path fill="#04534e" d="M289.2 257.3q-.1.6-1 .7c-.9.1-.9-.3-.9-.7q0-.8 1-.9c1-.1 1 .4 1 .9zm-4.6 1.6q.2-.6 1-.4 1 0 1 .8c0 .8-.8 1.1-1.3.9q-.8-.5-.7-1.3m5 2.7q0-1 1-1 1.1 0 1.2 1c.1 1-.8 1.2-1.5 1.2s-.7-.6-.7-1.2"/>
<path fill="#8cbebf" d="M285.8 262.7q.2-.7 1-.6t1 1c.1.9-.5.9-1 .9s-1-.8-1-1.3"/>
<path fill="#04534e" d="M292.5 264.5q0-.7.7-.8.8 0 1 .7-.1.8-1 .9a1 1 0 0 1-.7-.8m3-2.3q0-.6.8-.4 1.1.1 1 .8-.3 1-1.1.8-.7-.2-.7-1.2"/>
<path fill="#8cbebf" d="M289 256.9q0 .6-.7.6-1 0-.9-.6t.6-.6q1 0 1 .6m-2.5 1.9c0 .5-.7.7-1 .7q-.8 0-.8-.7t.8-.6q1.1 0 1 .6m1 3.8q.1.6-.5.7-1 0-1-.7-.1-.6.5-.7 1 0 1.1.7zm4-1.3c0 .4-.7 1-1.2 1s-.6-.9-.6-1.3.4-.7.8-.7 1 .6 1 1m5.8 1.3c0 .4-.7.4-1 .4q-.8-.1-.8-1-.2-.7 1-.7c.5 0 .8.8.8 1.3m-3.4 1.7q0 .8-.7.8a1 1 0 0 1-.8-.8q.1-.6.8-.6.9 0 .7.6"/>
<path fill="#231f20" d="M294.4 262s.8-.3 1.5-.3q.8.1.6.4t-.7.2l-1.4-.4zm-3.3 2.8s.6-.4 1.1-.5q.8-.2.8.4 0 .4-.6.3zm-2.2-5s1.4.6 1.7 1.1q.5.8.1.9t-.8-.5z"/>
<path fill="#231f20" d="m288.5 261.6 1.6-.3q.7 0 .7.2t-.5.3zm-3.4 0s1.2.2 1.5.5q.4.5.1.7c0 .2-.6.1-.8 0z"/>
<path fill="#231f20" d="M285 263.3s.4-.7.8-.8 1-.1 1 .1c.1.2-.4.5-.7.6zm-1-5.1 1.2.3q.6.5.4.7t-.8 0c-.5-.4-.7-1-.7-1zm2.6-3s1.2.4 1.6 1q.5.8.1.9l-.9-.4c-.3-.3-.8-1.4-.8-1.4z"/>
<path fill="#04534e" d="M300 263.7s-2.3 2-7.7 1.9-10.4-2.5-11.6-5l.5 1.2 1.2 1.6 3.8 2 4.3.9 3.3.2 3.2-.6 2.5-1.1.4-.7zm.8-2.5s-.9-1.2-2.2-2.3c-1.2-1.2-6.2-4.4-6.2-4.4l4 1.8 2.7 2.2 1.8 2z"/>
<path fill="#231f20" d="M301 256.4s1.4.5 1.6.8l.5.6s-.3 1-.6.6l-.8-.9zm4-2.2s1.3.5 1.8 1.1q.6 1.1.2 1.3c-.2 0-1-.7-1.2-1l-.7-1.4z"/>
<path fill="#231f20" d="M306.4 254s1.2.8 1.4 1.2.5 1 .1 1c-.3.2-.9-.4-1-.8l-.6-1.5zm6.8 1.5s1 .4 1 1.2v1.3l-.7-.7v-.7l-.3-1zm1.8.7s.8.7.9 1.2.2.8-.3 1q-.6-.1-.6-1zm-12.3 15s.6.2 1.2 0q.9-.4.9-1-.2-.8-.8-.2c-.4.4-.2.6-.5.8zm6.8 1.8s.9-.2 1.5-.9q1-.9.5-1.2c-.4-.2-1 0-1 .5zm5.5.1 1.3-.9q.8-.6.4-1c-.3-.5-.6-.2-.8 0l-.2.7z"/>
<path fill="#231f20" d="M316.7 273.1s.8-.9 1-1.4q.3-.9-.1-1-.7.1-.8.9z"/>
<path fill="#8cbebf" stroke="#04534e" stroke-width=".5" d="M299.9 264.1c0-2.4 3.5-8 7.5-8 1.6 0 4.9 1 7.5 2.1 2.2 1 3 2 4.4 2.4s3.7.1 3.7.1l5-.5-4 5.7-1.4 1.2s-1.2 2.4-4.7 3.8c-3.6 1.4-9.5.4-12.2-.3s-6-3.2-5.8-6.5z"/>
<path fill="#04534e" d="M299.9 264.1h.2q0-.8.6-2.1a12 12 0 0 1 2.7-3.8q1.7-1.7 3.9-1.8 1.3 0 3.4.6l4.1 1.4q1.5.8 2.4 1.5 1 .6 2 1l2 .2 1.8-.1 4.4-.5-3.6 5.2-1.4 1.2v.1a9 9 0 0 1-4.6 3.7q-2 .6-4.5.6c-2.8 0-5.9-.5-7.6-1a8 8 0 0 1-3.8-2.1 6 6 0 0 1-1.8-4h-.5q.1 2.6 2 4.4a9 9 0 0 0 4 2.3c1.7.4 4.8 1 7.7 1a14 14 0 0 0 4.7-.7 9 9 0 0 0 4.9-4h-.3l.2.1 1.4-1.3 4.3-6.2-5.6.7h-.1l-3.4-.1a6 6 0 0 1-1.9-1q-.9-.7-2.5-1.4l-4.2-1.5q-2-.6-3.4-.6h-.1q-1.5 0-3 1a11 11 0 0 0-3.3 3.5 8 8 0 0 0-1.4 3.7z"/>
<path fill="#0c8489" d="M324 264.9q-.7.4-1.4.3c-.5 0-11.6-6.7-15-6.4s-6.8 1.7-6.9 5.3c0 3.5.8 3.5 1.5 4.5s7.5 2.5 7.5 2.5h4.1l3.3-.4 3-1.6 1.5-1.3 1.1-1.3z"/>
<path fill="#04534e" d="M306.5 263q0 1.3-1 1.3t-1.1-1c-.1-1 .4-1 1-1q1-.1 1 .8zm1.1-3q.2-.9 1.2-.8 1.1 0 1.2.8 0 1-1.2 1-1 0-1.2-1m6.7 1.8q0-.8 1-.9.8 0 .9 1c.1 1-.4.8-1 .8a1 1 0 0 1-1-.9zm1.3 4q.1-1 1-1t1.2.8q.2 1.3-.8 1.3c-.6 0-1.4-.4-1.4-1zm-3.2 2.7q.1-1 1-1 1.1 0 1.2 1-.1.8-1 .9-1.1 0-1.2-1zm-6-1q0-1 .9-1a1 1 0 1 1 0 1.9 1 1 0 0 1-1-1z"/>
<path fill="#8cbebf" d="M306.4 263q0 .9-1 1a1 1 0 0 1-1-1q0-.9 1-.8c1 .1 1 .2 1 .7zm3.3-3.2q0 .8-.8.9c-.5 0-1.3-.4-1.3-.9s.5-1 1-1q1 .1 1 1zm6.4 1.7q0 .8-1 .9c-1 .1-.9-.4-.9-.9q0-.7 1-.8c1-.1.9.4.9.8"/>
<path fill="#04534e" d="M312.7 264.2q0 1.1-1 1.2t-1.1-1c-.1-1 .3-1 1-1s1 .2 1 .8z"/>
<path fill="#8cbebf" d="M308 267.2q0 .9-1 1c-1 .1-1-.5-1-1s.6-.8 1.2-.8.8.3.8.8m4.5-3a1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1 1 1 0 0 1 1 1m5.2 1.3q0 1-1 1t-1.1-1c-.1-1 .2-.8.8-.8q1.1 0 1.3.8m-3.3 2.7q0 1-1 1-1.3 0-1.1-1 .1-.8 1-.8t1 .8z"/>
<path fill="#231f20" d="M314.8 265s1.6-.2 1.9 0q.4.6.1 1c-.1 0-.8.1-1-.1 0-.3-1-1-1-1zm-1.7-5.4s1.3.3 1.8.9q.6.9.4 1.1-.4.3-1-.2zm-3.7 2.6s1.5.8 1.8 1.3q.5.6.2 1c-.1.2-.8 0-1-.3 0-.3-1-2-1-2"/>
<path fill="#231f20" d="M308.7 263.3s1.7.2 2 .5q.8.4.8.6-.1.4-.5.4l-1.2-.5-1-1zm-1.9-4.8s1.7.2 2 .5.5 1 .1 1.2q-.6.1-1-.5l-1-1.2zm-3.6 2.5s1.5.8 2 1.4q.6.6 0 1c-.4 0-.9-.7-.9-1z"/>
<path fill="#231f20" d="m302.8 263 1.5-.2c.5 0 1.2-.1 1.2.2s-.6.5-1 .4zm8.6 5.6s1.1-.7 1.7-.6q.7 0 .7.4c0 .4-.7.3-.9.3zm-6-.9s.4-.6.9-.7q.6 0 .7.2-.1.4-.6.6h-1z"/>
<path fill="#04534e" d="M322.6 266.6s-1.6 2-4 3a14 14 0 0 1-7.2 1.1 15 15 0 0 1-11.2-5.2l.4 1.1 2 2.6 2.3 1 3.5 1 3.3.3h2.5l3-.5 1.9-.7 1.3-.8 1-.8 1.2-2zm-3-5.9s-1 .3-3-.9-4.3-2.8-8.8-2.8-5.9 3-6.2 3.2l2-2.6 2-1.1 1.7-.3h1.5l2 .6 1.7.6 2.1.7 1.8 1 2.2 1.1zm6 1.4h-3.4c-.8 0-.8.2-1 .4l-.2.4-.8-1.7 3.5-.5 2 .7-.2.6z"/>
<path fill="#0c8489" stroke="#04534e" stroke-width=".5" d="M320.7 280.4q.3.9 1.1 1h1.6l3.6-.3 1.2-1.4.8-3.2 1-1.2 3.3-1.9 2.8-.8 1.9-.2 2.5-.8 1.9-1 .8-1.2.8-1.8-.8-2.5-2-2.7-3.4-1.3H335l-3 1.1-5.5 1.3s-3 1.3-4.3 3.7c-1.3 2.3-1.2 3-1.5 6-.2 3.2 0 7.2 0 7.2z"/>
<path fill="#8cbebf" d="M322.2 279.2c1 0 .2-1.7 1.4-3.8s2.6-4.6 3-4.6.4.4.9.3 1.2-.8 1.2-1 0-1.3 1-2c.8-.8 2.1-1.9 2.7-2q1-.3 1 0c.1.3-.3.7.1.8s1.4.1 1.6-.5l.2-1s.6-.5 1.8-.3 1.3.2 1.4.7-.5.5-.4.8v.6c.2.2.4 1 1.2 1 .8-.1 1.1-.6 1.1-.6l.3-.4s.2-.5-.1-.7q-.4-.4 0-.3c.4.1 1.6 1.8 2 1.1s.5-1.7.2-2l-1.9-2.7-3-1-4 .3-4 2.2-3.6 2s-.2 2.9-.8 2.3c-.7-.5-1.6-2-1.9-1.5s-.9 2.7-1.3 3.8c-.3 1-1.4 4.4-1.3 5.6s.8 2.9 1.2 2.9"/>
<path fill="#8cbebf" d="M325.8 274.4q-.6.1-.8.5-.3.3-.2 1c0 .3.7.7.7.7h.5l1-.3.3-.8q-.1-.7-.4-.6zm6.2-5c-.6-.3-.8.1-1 .3v.5s.2.8.4 1c.3 0 1.1.3 1.3 0q.5-.2.5-.9c0-.7-.7-.7-.7-.7zm5 1.1q-.1-.3-.9-.2-.6 0-.6 1 .4.9 1.2.6c.5-.1.4-.8.4-.9zm-14.4 1c0 .3.5 1 1.2.9a1.3 1.3 0 0 0 1.2-1c0-.4-.4.3-1 .5s-.7.2-1 0q-.3-.5-.4-.4"/>
<path fill="#04534e" d="M327.3 275.5s0 .6-.4 1q-.7.4-1 .3-.5 0-.8-.3l-.3-.7s.5.8 1.3.6a2 2 0 0 0 1.2-.9m13.3-8.3s0 .6-.5.9q-.6.4-1 .3-.4 0-.7-.4c-.6-1-.3-1.5-.3-1.5s.4 1.6 1.3 1.5a2 2 0 0 0 1.2-.8m-11.9 2.9s-.4 1.2-1.2 1.2c-.9 0-.8-.2-.7-.4 0-.1.3.2.7 0 .4 0 1.2-.8 1.2-.8m2.3-.3s0 1 .2 1.2q.5.5 1 .5.6.1.9-.4.3-.8.1-.8l-.2.5q-.4.4-.8.3-.6 0-.8-.3zm4.3-4.3s.2.7-.3 1.2-1.5.2-1.5.2.8.3 1.2-.2.6-1.2.6-1.2"/>
<path fill="#04534e" d="M321.7 281.1s5.3 0 5.7-.8c.4-.7-.4-3.6 2.7-5.8 3.2-2.2 5.7-2.2 7.4-2.3 1.7-.2 5.3-1.3 6-4.5.3-2-2.3-2.2-3.3-4-1-2-3.6-1.2-3.8-1.2s-3 0-4.3 1.3c-.9.8-.8 1.8-1.1 2.2s-4 0-4.3.6c-.4.6.1 1.7-.2 2-.2.3-1-.8-1-.8l.2-2.8 4.5-2.6 5-2.8 3.5.1 3.5 1.8 1.7 2s.3.8.5 2.3l-.2 2c-.8 2.8-3.7 4.8-6.7 5a14 14 0 0 0-7 2.5c-1.4 1-2.1 5.3-2.1 5.9 0 .5-6.6.1-6.6.1z"/>
<path fill="#231f20" d="M324 270.8q-.6.4-.8 0c-.2-1 .3-2.2.3-2.2v1.6q.3.1.5.3zm3.2-.6c-.2-.1-.1-1 0-1.3.2-.3 1.7-1.4 1.7-1.4l-.6 1.5c-.2.4-.7 1.3-1 1.2zm-1.2 5.3q-.4 0-.3-1c.1-1 1-1.6 1-1.6l-.4 1.2.2 1q-.1.4-.5.4"/>
<path fill="#231f20" d="M328.4 274s-.5.6-1 .9q-.9.6-1.4.6c-.4 0 .1-.8.3-.9zm4.5-6.4s-.6.5-.9 1.2q-.5 1.2-.1 1.4c.3.2.7-.8.8-1.1z"/>
<path fill="#231f20" d="M334.6 269.9s-.5-.4-1.2-.6q-1-.2-1.2.2c0 .2-.6.5-.3.7s.6-.3.8-.3q.3-.3.9-.2zm-1-4.5c.3.3.7-.2 1-.6s.7-1.2.7-1.2l-1 .7c-.4.3-.9 1-.7 1.1m6.9-.1s-1 .4-1.2.8q-.6.6-.4 1c.3.3.6-.2.8-.5zm-2 4.6s-.3.6-.9 1q-1 .6-1.3.3 0-.4.3-.7l.8-.2z"/>
<ellipse cx="328.8" cy="207.4" fill="url(#mx-b)" rx="2.9" ry="4.3" transform="rotate(8.7)"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m295.4 247-1.7 4.2 1.4-.2.7-3.8z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M296.1 245.1q-1 0-1.2 1 0 1.2 1 1.4 1 0 1.2-1 0-1.2-1-1.4z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m369.7 255.8-4.2 4 1.7.4 2.9-3.8z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M371.6 254.1a1.5 1.5 0 0 0-2 .6q-.7 1.1.4 2 1.2.4 2-.7c.4-.6.2-1.5-.4-1.9z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".3" d="m262.8 233 2.2 4.9 1-1.4-2.6-3.7z"/>
<path fill="#fcca3e" stroke="#aa8c30" stroke-width=".2" d="M261.9 230.9q-.9.8-.1 1.9c.4.6 1.3.7 1.8.2q.9-.9.1-2a1.3 1.3 0 0 0-1.8-.1z"/>
<path fill="#aa8c30" d="M296.6 245.3s.3.1 0 0c-.5 0-1.4.4-1.4 1a1 1 0 0 0 1 1c.5 0-.2.2-.2.2l-.7-.2-.2-.4-.2-.5.1-.6.4-.5.5-.2h.3z"/>
<path fill="#aa8c30" d="M295.6 248.1s-.3 0-.5.3l-.7 1.1 1-2.3.4.2zm76.4 6.4s.3.4-.1 0c-.4-.3-1.8-.2-2.1.5q-.5 1.2.6 1.7c.6.2-.2 0-.2 0l-.7-.5-.1-.6v-.7l.5-.6.7-.3h.7l.3.2z"/>
<path fill="#aa8c30" d="M369.4 257.3s-.3-.2-.8 0l-1.4.9 2.3-2.2.5.6zm-107-26.7s.4-.1 0 .1-.8 1.4-.3 2q1 .7 1.7 0c.4-.5 0 .2 0 .2l-.7.4h-.6l-.6-.3-.4-.6v-.8l.2-.5.3-.3.3-.2z"/>
<path fill="#aa8c30" d="M264 233.8s-.3.1-.2.6l.3 1.5-1.2-2.7.7-.2z"/>
<ellipse cx="337.6" cy="-128.1" fill="url(#mx-c)" rx="4.6" ry="3.1" transform="rotate(62.9)"/>
<path fill="#e92736" d="M262.6 241.2s1 .2 1.5 0 .6-.7.7-.8.3-.6.2-1l-1-1.4-1-.1q0-.2-.2-.5c-.3 0-1.7 1.6-1.5 1.8.3.3.4 0 .5.1v1.1c0 .3.8.8.8.8"/>
<path fill="#e92736" d="M264 238.1c.1.3.7 1 .9 1q.5.3 1 0c.3 0 1-.7 1-1l-.1-.7c-.2-.3-.1-1-.4-1.1q-.2 0-.5-.3v-.7c-.3-.3-2.6 1.1-2.4 1.4q.3.1.5.1z"/>
<path fill="#e92736" d="M269.2 237.6c.2-.4.1-1.5-.1-1.8s-.6-.3-.7-.4q.1-.1.3-.5c.2-.4-2.6.1-2.4.4s.6.1.6.3q-.2.3-.5.6l.3 1.1q0 .3.2.5l.6.2h1.4q.2 0 .3-.5zm22 12.8s.3.7.8 1h1l.6-.5c.2-.2.4-1.2.3-1.4 0-.2-.5-.6-.5-.8q.2-.2.2-.4c-.1-.2-2-.2-2 .2q.2.3.2.3c0 .2-.5.5-.6.7-.2.1 0 1 0 1z"/>
<path fill="#e92736" d="M293.8 249.5c0 .2-.2 1 0 1.1q.1.5.5.8h1.2l.4-.6c.1-.3.6-.7.5-.9q-.2-.2-.1-.4.2-.2.3-.5c.1-.3-2.1-1-2.2-.7q.2.3.2.4c0 .2-.7.6-.8.8"/>
<path fill="#e92736" d="M297.3 252.5c.3-.2 1-.9 1-1.1q-.2-.6-.2-.7l.5-.1c.1-.2-1.6-1.6-1.7-1.3q.2.4.2.5l-.7.1q-.3.3-.5.8-.2.2-.2.5l.2.4c.1.2.8.9 1 .9zm66 5.5s0 .8.4 1.2q.5.4.8.4h.7l1-1q-.2-.6-.2-1c0-.4.3 0 .3-.2s-1.6-1.1-1.7-.8c-.1.2.2.3 0 .4l-.8.2c-.2 0-.5.8-.5.8"/>
<path fill="#e92736" d="m366 258.5-.8.8q0 .5.2.8c0 .2.7.6 1 .5q.2 0 .5-.2c.2-.1.8-.2.8-.4l.1-.4q.2 0 .6-.2c.2-.2-1.2-1.7-1.4-1.5q-.1.3 0 .4c-.2 0-.9 0-1 .2"/>
<path fill="#e92736" d="M366.4 262.2h1.4l.4-.5q0 .1.4.2c.2 0 0-2-.3-1.8q-.2.4-.2.4-.2-.2-.5-.3l-1 .1s-.2 0-.3.2l-.2.4v1q0 .2.3.3"/>
<path fill="#f7e204" d="M264.2 239c.2.4-.1 1.2-.3 1.4s-1.3-.1-1.3-.3q0-.4 0-.8c.2-.2.6-.7.9-.6q.5.1.7.4zm.3-1.3q-.2.5.2.7c.1.2 1 .4 1.2.3s.6-1.5.4-1.7q-.4-.2-.7-.2c-.2 0-1 .6-1.1.9m2.7-1.3v.7q.4.3 1 .5c.3 0 .5-1 .5-1.1q0-.3-.3-.4l-1.2.2zm26.1 13.7c0 .4-.7.7-.9.7s-.7-1-.6-1q.2-.4.5-.5.5-.1.9.2zm1.1-.6q-.3.2-.3.6c0 .2.3.8.5.9s1.2-.5 1.3-.7q0-.4-.4-.6l-1-.2zm2.4 1q-.3.1-.4.5s0 .6.3.8.8-.2.9-.3v-.4c0-.2-.8-.6-.8-.6m68.6 8.2c-.2.2-.9.2-1 0-.3 0-.3-1-.1-1q.2-.3.6-.2.5 0 .6.6zm1 .1q-.5 0-.6.3c-.1.1-.2.7 0 .9.1.1 1.2.2 1.3 0v-.5zm1 2h-.5q-.3.1-.4.7c0 .2.7.3.8.3l.3-.2-.1-.8z"/>
<ellipse cx="438.6" cy="-93" fill="url(#mx-d)" rx="2.2" ry="4.5" transform="rotate(47.9)"/>
<path fill="#a8ac71" d="M285.8 199.3s-3.8.3-6.5-.1-2-7.3-2.1-7.5c0-.2-1.6-1.8-1.4-3.3s5.2-5.5 5.8-5.8c.5-.1 1.5.3 1.5.3s1.2-1.2 1.6-1.2.9.7.8 1c-.2.3-1.9 1.3-2 2-.2.8 0 1.5-.5 2.3s-1.4 1.7-1.5 2.4-.4 1.1 0 1.4 2.1 0 3.5-.6 2-1.5 2.1-1.4c.2 0-.8 1.7-2.3 2.4s-2.5 1.3-3.3 1.2c0 0-.8 2 1.8 2.2 2.6 0 4-.6 4-.6zm-15 16s2 .8 2 3-2.1 4.1-2 8c0 4 .6 4 5.8 8.3a6 6 0 0 1 1.6 2.5c.3 1.3 1.2 8.9 6.2 8.9s5.8-3.3 5.8-3.3l-2.2-3.1s-1.7.5-2.7.3a3 3 0 0 1-2.3-2.1c-.6-1.2-.7-4.1-1.6-5.4-2-3.2-5.8-3.7-5.9-7 0-3.4 2.3-3.7 1.7-7.7-.7-4-5.4-5.8-5.4-5.8l-1.9-.8-.7 4z"/>
<path fill="#f1a720" d="M284.7 187.6c1.7-.4.7-4.8.7-4.8l-1.2 1s.8 1 .8 2-.3 1.8-.3 1.8"/>
<path fill="#a8ac71" d="m287.8 231.5-.9-.7c-.6-.4-3.2-2.6-2.8-8.9.5-7.2 11-11 11.6-13.8.8-3.2 3.1-4.5-5.2-11l-2 2.2 3.5 2.6s.7-.4.8.6c0 1-1 1.7-1.2 2.5-.2.7-5.4 4.3-5.8 4.6l-3 2.4s-1.3 1.3-2.5 4.1c-1.3 2.8-.7 6.4-.7 6.4s0 7.6 8.2 9"/>
<path fill="#78732e" d="m282.7 235.5.6 2.7-1.7.6h-.7l-.5.2-1.3 1.2h-.3s-.2 0-.3-.8q-.1-1.4-.2-1.5l1.6.7h.6l.3-.1.7-1.5zm-4.8.5-.8-1-1-.9 1.2-.6h1.2l-.2 1.5zm2.7-4.3s.8.3 1 1l.6 1.2-2-.2-1.4-.4 1-1.4zm-.7 11.9s-.4-.3-.7-.9l-.4-1.6 1.3-.3 1.5.7h.4l-.3.8-1.5 1.3zm5-4.1s-1-.4-1.2-1q-.3-.6-.4-.4l-.3 1.5-.5 1.5.2.2 1.1-.3.9-.9.3-.6zm-4.7 4.8 1.4.7q1.2.6 1.2.5l.6-.6.7-1.3v-.1l-3 .3zm4.4-1.2 2.7-1.6.1-1.4-.5-.4h-1.5l-.5 1.8zm-1.8 2.5s1.8.8 5-.4c1.4-.5 1.7-1.2 1.7-1.2l-1.3-.5-1.2 1zm-2.5-20.3-.4-1.4q-.2-1.2-.1-1.1l.9.5 1.3 1-1 .7zm2.2 3.7-.9-1-.6-1.2 1 .2 1.1.6z"/>
<path fill="#c6c7a6" d="M276.6 187c-.2.7-.4 1.9.5 2 .4.2 2.3 0 4.5-2.4 1-1.2 1-2.4 1-2.4l.5-1.3s-.9-.6-2.2.2-4.3 4-4.3 4z"/>
<circle cx="282.5" cy="183.6" r=".4" fill="#1c242f"/>
<path fill="#a8ac71" d="m279.7 184-1.7 1.7s1.4-.4 2.6-1.5c.6-.6 2-.8 2.6-.7.4.1.2-.4 0-.5-.3-.3-1.4-.4-1.6-.3z"/>
<path fill="#78732e" d="M284.1 224.5v-2.2l-.9.8-.8 1.1 1.3.5zm-4.5-2.6v-2.3c0-1 .5-3.1.5-3.1l.6 1.4 1.5 2.4.5.4-1.3 1-1.4.4zm4.5-.6.3-1c.1-.6.5-1.4.5-1.4v.1l-2 1.7.8.7h.5m-3.7-6s.2-.6 1-1.5l1.3-1.8.6 2.4.4 2v.4l-.7.2c-.2 0-1.6 0-1.8-.3zm4.8 2.9.8-.9 1-1-3.2.6.5 1zm-2-6.7s.3 0 1.2-1c.8-.8 1.3-.7 1.3-.7l.3 3.4-1.6-.5z"/>
<path fill="#78732e" d="m287.3 216.2 2-2-3.3-1 .6 2.3zm1.4-5.2.2-3.3s-.7.3-1.5 1l-1.2.9.4.6 1 .7h1zm2.8-3.2V205s-.5.8-1 1.2l-.9.8.9.6h1zm1.2 3.5 1.6-1.3 1.5-1.7-4.2-.3.2 1.7 1 1.6zm-9.6-13.8-.2 1.8h3.5l-.9-1.2-1-.6-1-1zm6.6 16.2c.2 0 1.1-.8 1.6-1.2.4-.5 1.2-1 1.2-1l-3.3-.3v2.2zm6.3-5.9s.4-1.3.3-2.6q-.1-1.8-.2-1.8l-2.5 1.7.4 1.2 1 1z"/>
<path fill="#78732e" d="M291.8 204.8s.7-1 .8-1.4v-.4h2l1.1.2h.2l-.9 1.1-1.2.7-1.5.2zm4-2-1.3-2q-.7-1-.8-1l-1 2.7v.3h3z"/>
<path fill="#fff" d="m271.6 222.2-.3.8s1.3-.7 2.3-.7 2.1 1.3 2.1 1.3l.2-.9s-1.3-1.3-2.2-1.3c-.8-.1-2 .8-2 .8zm.7-5.8.3.4c0 .1.1-1 1.4-1.5 1.4-.6 2-.1 2-.1s-.2-.8-.8-1q-.8-.2-1.8.5a4 4 0 0 0-1.1 1.7m4.4 4.4s-.6-1.4-1.6-1.6c-1.1-.3-2.5.5-2.5.5l-.2.7s1.5-.8 2.5-.4c1.1.5 1.5 1.7 1.5 1.7l.3-1zm-5-5.1.4.5s-.2-1.4.5-2.1l1.1-1-.6-.3s-.9.5-1.2 1.1a3 3 0 0 0-.2 1.8m-.5-2.3c.3-.8 1.2-1 1.2-1l-.3-.3h-.5s-.5.3-.9 1c-.3.9-.3 2-.3 2l.6.3s-.1-1.4.2-2m7.8-19.2q1.4-.4 1.5-.3c.1.1-1.3 1.3-.7 2.9q-1 2.1-.9 2.3h.7l.4-1.8c.8 1.3 2 2 2 2h1s0-1.3.4-2.4h.1a5 5 0 0 0 2 1.5v-.4s-1.1-.6-1.7-1.5l-.1-.1a4 4 0 0 1 1.8-1.7l.7.6.2-.2-.5-.7h-.6l-1.8 1.7-.8-1.5-.4-.2s-1.1.1-2 1.7c-.2-1.2 1.1-2.4 1.1-2.4v-.5l-2.4.6c-1 .3-1.7 1.2-1.7 1.2v.8s1-1.1 1.7-1.6m1.5 2.2c.6-1 2-1.5 2-1.5s0 .7.7 1.7c-.5.9-.8 2.2-.8 2.2s-1-.6-1.9-2l-.2-.3zm-7.6 21.1v.8s.8-.9 2-1 2.3 1 2.3 1 0-1-.2-1.2c-.1 0-1.1-.6-2-.6s-2.1 1-2.1 1m21.4-11.2-.5-1 .6-.3 1.7-1.6-.4-.6h-1.6l-1.4-.1.5-1.7.6-1.4-.6-.5s.2.5-.8.6-2.8-.8-2.8-.8v.4l2.5.7q.9 0 1.2-.3l-.5 1.3-.3 1 .2.2v.8l1.4.1 1.6.1s-.8 1-1.7 1.5h-.3l-1-1.5-.1.5.7 1.2-1.5-.1-.4.4v2.5l-1.8-.6-.3.3v-.1l-.4.4s-.4 1.4-.3 3.1v.1a5 5 0 0 1-2.4-1.3l-.4.3s-.3 1.4 0 3.2c-1.3-.3-2.5-1.5-2.5-1.5l-.5.5.7 4.7h-.7c-1.4 0-2.3-1.3-2.3-1.3l-.4 1s.6 2.7 2 4l.4.2c-1.3 1-2.8 1.2-2.8 1.2v.9l2.2 1.5-1.6 1 .2.5 1.8-1.3 1.8.5v-.5l-1.5-.3.1-.1c.5-.4 1.4-1.8 1.4-1.8v-1s-.5 0-1.1-.6c1.8-1.2 1.9-1.8 1.9-1.8l.4-.6s-.7-.3-1.1-1.2l2.9-.7.2-.2s-.7-1.2-1-2.6l3 .6.4-.4-.5-2.4c1.8 0 3.2.2 3.2.2l.3-.3s-1-1.9-1-3h4l.3-.5s-1.4-1-1.7-1.5m-10.5 15.5s-.5 1-1.4 2l-.2.2-.5-.2q-1.5-1.1-1.4-1.3l2.5-1.6zm.9-3.2-2 2-.4-.5c-.8-1.2-2-2.1-1.8-3.8 0 0 .7 1.2 2.2 1l1-.1c.4 1 1 1.4 1 1.4m2-2.5-2.7.7-.1-.5c-.5-1.6-.5-4-.5-4s1 .8 2.6 1.2zm-.5-3c-.2-1.4.1-2.9.1-2.9s.8.8 2.3 1q.2 2.2.3 2.1zm5.8-2.2s-1 .4-2.8.1v-3.5l2.3.7zm0-3.1h-.2V205s1 .3 1.7.2c.2.4 0 .5.5 1.2s1.1 1.3 1.1 1.3-2.3.2-3.2 0zm-7.6 18.6-.1-.5s-1 .7-1.2 1.4l-.3-.1-2-.8.2.4 1.8.8h.2l-.5 1.4.2.3.5-1.7 1.2.1-.2.8-.4 1.5.5.3h2l-.3-.4-.7-.1h-1.1l.3-1.2.3-.8-.3-.7-1 .2zm-11.3-1.3c1.3 0 2.3.9 2.3.9v-.6s-1-1.4-2.1-1.4-2.5 1.2-2.5 1.2v1.2s1-1.3 2.3-1.3m13.3 19.4c-1.3.6-2.7.6-2.7.6l.8-1.5c2.5-.8 3-2.4 3-2.4l-.2-.7s-.6 1.6-2.6 2.4c.4-1.2.7-3.2.7-3.2l-.7-.2s-1 1.3-2 1.6a8 8 0 0 0 .8-2.7l-.3-.5s-.7.6-1.7.8h-.6c.6-.8 1.7-3.2 1.7-3.2l-.2-.5s-.8 2.1-1.7 3.2l-.3.4c-1-.3-2.2-1-2.2-1l.1.3s.8.6 1.8 1l-1.3 1.2v1l3 .4c-1 1.6-2 2.1-2 2.1l.5.7s1.3 0 3.7-.6q-1 1.8-1.2 1.8c-.2 0 2.2.2 3.7-.6s2-1.6 2-1.6l-.4-.4s-.4 1-1.7 1.6m-6.9-4 1.2-1.5c1.6.3 2-.1 2-.1s.3.8-.6 2.3c-1.1 0-2.6-.8-2.6-.8zm1.4 3 1.3-1.6.3-.3c1.5 0 2.3-1.3 2.3-1.3s.2 1.2-.5 2.7c-1.9.7-3.4.5-3.4.5m-1.9-9.8c1.5 0 3.3.7 3.3.7l-.2-.7-2.9-.6a4 4 0 0 1 1.6-1.6l-.5-.3s-.6.1-1.4 1.5l-.2.4h-.3a5 5 0 0 0-2.4.7l.2.4s1.1-.5 2.3-.5l-.7 2.4.3.6zm-3-6.2-.3-.7s-1.5-.6-2.7 0c-1.1.5-2 1.9-2 1.9l.5.9s.8-1.5 1.9-1.9 2.6-.2 2.6-.2m3 2.4-1-.6s-1.7.5-2.2 1.1-1.1 2.1-1 2.5q.5.7.7.7c.2 0 .3-1.5 1.1-2.3s2.4-1.4 2.4-1.4m-3.9-.5a5 5 0 0 1 2-1l-.7-.5s-1.5.2-2.2.9-1.8 2.1-1.8 2.1l.9.9s1.2-1.8 1.8-2.4m5.2-44.7c.8-1 1.6-1 1.9-1h.6l-.8 2.5c-.2 1-.7 1.2-1 1.9s-1.1 1.8-1.2 2.8q.1 1.5.5 1.7c.3.1.8-.4.7-.7 0-.3-.3 0-.4 0q-.2 0-.3-.9c-.1-.6.7-2.2 1-2.8s1-1.1 1-2l.7-2.5q0-.1-1.3-.3-1.1 0-1.9 1.2c-.4.6-1.7 1.1-1.7 1.1s1.4 0 2.2-1"/>
<path fill="#78732e" d="M285.2 194.5h-2.5l.7 2zm-3-.1-.5-.4-.3-.3-.9.5-.5 1.9.3.3 2-2zm-.3 5-1.8-2.7-1 2.2.5.2 1.2.2h1.1zm7.4 44.7a9 9 0 0 1-4.8 1.5 7 7 0 0 1-3.4-1 4 4 0 0 1-1.5-1.7q-.8-1.7-1-4-.3-2-1.1-3.6c-.8-1.3-2.4-2.1-3.8-3.3a7 7 0 0 1-2.8-5.2v-.8q.1-2.5 1-4.5c.9-2 1.2-2.5 1.2-3.5v-.5q-.6-1.1-1.5-1.9c-.9-.8-1.2-.8-1.2-.8l-1-.2-.1.3 1 .2v-.1.1l1.3 1q.9.7 1.1 1.5v.4q-.1 1.4-1 3.3a12 12 0 0 0-1.2 4.7v.8a8 8 0 0 0 3 5.5c1.5 1.1 3 2 3.7 3.2q1 1.9 1.3 5 .3 1.3.8 2.5c.5 1.2.9 1.5 1.6 2a7 7 0 0 0 3.7 1c2.7 0 4.9-1.6 4.9-1.6z"/>
<path fill="#78732e" d="m287.2 239.2-1.3.4q-.8 0-1.7-.8a4 4 0 0 1-1.2-2.3q-.1-1.3-1-3.3c-.5-1.5-2-2.6-3.5-3.7-1.3-1.1-2.6-2.2-2.9-3.4v-.7q.1-1.5.8-3.1a11 11 0 0 0 .9-4.8c-.2-2-1.7-3.5-3.2-4.5l-2.8-1.4-.1.3h.2c.5.3 1.8.9 3 1.8q2.1 1.4 2.6 3.8v.8q0 2.2-.9 3.9c-.9 1.7-.9 2.2-.9 3.2l.1.8c.3 1.3 1.6 2.4 3 3.6 1.5 1 3 2.2 3.5 3.5q.8 2 .9 3.2.1 1.4 1.2 2.5 1 1 2 .9l1.4-.4z"/>
<path fill="#a8ac71" d="M296.4 231.6s1.7.4 1.6 1.8a13 13 0 0 1-.8 3.4l-2.4-2.2 1.3-1.2.5-.7z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M266.4 214.5s-1.7.6-2.1-.1c-.4-.8-.2-1.4-.2-1.5s-1-.3-1-1.3c.1-1 1.4-.9 1.6-.9s.4-1.5 1.3-1.4c.8 0 1.2 1 1.2 1z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M268.3 215s-1.5.8-2 0c-.4-.7 0-1.6-.1-1.8-.2-.1-1-.4-.9-1.3.2-.9 1.1-.6 1.4-.8.3-.1.6-1.4 1.5-1.2.9.3 1 1 1 1l-1 4.1z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M270.8 215.3s-.5 1-1.5.8-.7-1.7-.9-2c-.1-.3-.9-.5-.8-1.4.2-1 1.1-.9 1.5-1s.7-1.6 1.4-1.4 1.3 1.1 1.2 1.5c0 0-1.5.5-1.7 1.5s.8 2 .8 2z"/>
<path fill="#af7029" d="m293.7 231.2-2.2-2.6c-.9-1.3-2.3-2.7-2.3-2.7l-2.2-1.1-1.4-2.4-.2-2.8.6-1.6.8-1.2s-4.6 1.7-2.2 10.5c1 3.8 3.5 4.3 3.5 4.3l4.3-.3h1.3z"/>
<path fill="#816c2a" d="M291.6 201.7q.5 0 .8.4l.2.6v.4c-1.6 3.4-4.8 5-7.8 7a14 14 0 0 0-3.8 4 12 12 0 0 0-1.6 6.8c.1 5 2 7.8 4.2 9.3a10 10 0 0 0 5.2 1.5h.4l1.4-.1 3-.2 1.8.1.9.4.2.5q0 .6-.6 1.2l-1 .7.2.3 1-.8q.6-.6.7-1.4 0-.3-.3-.7t-1.1-.5l-1.8-.1c-2 0-4.5.3-4.5.3v.2-.2h-.3a9 9 0 0 1-5-1.5c-2.1-1.4-4-4-4-9v-.4q0-4 1.5-6.3c1.4-2.4 3.7-3.8 6-5.3a14 14 0 0 0 5.5-5.7v-.5q0-.4-.2-.8t-1-.5z"/>
<path fill="#fff" d="M297.8 232.7s-1 2-1.4 2c-.2 0 1 .3 1.3.8l-.3.5s-1.2-.9-2-1v-.6l1.1-.7a4 4 0 0 0 .9-1.7z"/>
<path fill="#fcca3d" d="m302 234-2.7.8-1.3-1s0-1-.5-1.5l-1.6-1c-.6-.2-2.5-.2-2.5-.2l-5-5.3 2 .2 2.8-.4 2.7-.2 1.4.2 1.6.4.8.7.8.8.8 1.3.6 1.6.2 1.7v1.9z"/>
<path fill="#af7029" d="M302.2 233.8s-1 .3-2.2.3q-2-.1-2-.2l-.2.9q-.4 1.3-.5 1.3v3.2l2.6.5 1.4-.5.7-3.5z"/>
<path fill="#fcca3d" d="M301.5 232.5v1.2l.6.2v-1.2zm-2.5-30-.3-2.1-.8-1.5-1.1-1.5-1.2-1.3-.6-.5-.3-.5-2.9.2-1.4 1.7s3.8 2.8 5.3 5.8a7 7 0 0 1 2.8 1.7l.1-.2z"/>
<path fill="#af7029" d="M296.3 205q-.2 3.1-.8 3.7l1.8-2 1.2-2.2a7 7 0 0 0-2.8-1.7q.6 1 .6 2.1z"/>
<path fill="#816c2a" d="m300.3 237.9-1.4-1.6q-1.1-1-1.3-1l-.2.4h.1l1.2.8 1.3 1.6z"/>
<path fill="#6f5b24" d="M302 235.5s-.3 1-1.3 1.8c-1 1-2.2 1.5-2.2 1.5l2.8 1 1.9-1.7z"/>
<path fill="#404118" d="m302.4 237.8-.2.3-.5.5-.4.5.6-3 .6 1v.4z"/>
<path fill="none" stroke="#78732e" stroke-width=".2" d="M285.8 199.3s-3.8.3-6.5-.1-2-7.3-2.1-7.5c0-.2-1.6-1.8-1.4-3.3s5.2-5.5 5.8-5.8c.5-.1 1.5.3 1.5.3s1.2-1.2 1.6-1.2.9.7.8 1c-.2.3-1.9 1.3-2 2-.2.8 0 1.5-.5 2.3s-1.4 1.7-1.5 2.4-.4 1.1 0 1.4 2.1 0 3.5-.6 2-1.5 2.1-1.4c.2 0-.8 1.7-2.3 2.4s-2.5 1.3-3.3 1.2c0 0-.8 2 1.8 2.2 2.6 0 4-.6 4-.6z"/>
<path fill="#ab6d29" stroke="#4d2a15" stroke-width=".4" d="M318.7 170.6s-7-1.3-7 4.5c.1 1.9.5 2.5 2 3 1.5.3 4.4.5 7 2.7s2.7 4 2.7 5.3c0 1.4-.7 2.6-.7 2.6l1.6 7.7 1.6 5.7 4.8-2.7 2.8-8.5-3.6-15.2z"/>
<path fill="#4d2a15" d="M325.6 200s2.5-1 3-3c.3-.5-2.4-2.9-2.4-2.9-.1 0 1.3.7 2 1.3q1 1 1.3.8c.2-.1 1.3-2.7.9-3.3s-1.1-2-2.1-2.6-2.4-.6-2.4-.6 1.9-.6 2.8 0c1 .5 1 1 1.1.9.1-.3-.2-2.9-.6-3.7q-.4-1.3-2-1.6c-1.1-.2-2.3-.1-2.3-.1s1.6-.9 2.8-.6c1.3.3 1.3 1 1.4.8s-.6-2.4-1-3a7 7 0 0 0-2.1-1.8l-1.8-.2h-.6s1-.6 2.1-.5 1.3.5 1.4.4q.3 0-.8-.7c-1.1-.7-4.2-2-4.2-2h-3.3s0-.8 1-1q1.3 0 1-.1c0-.3-2.6-.8-2.6-.8H316l-3 .3s.2-.5.7-.9q.6-.5 1.3-.6c1-.3-.6-1.1-.6-1.1l.9-1.3 3.9.3 8.2 4.8 3.9 3.3 2.4 6.2-.7 10.8-5.5 4.5-2.4.3z"/>
<path fill="#d2a567" d="M318.4 173.3s-.2-.7-.2-1.7.2-.9.2-.9-2.7-.3-4.6 1.2-.8 4.1-.8 4.1.8-.8 1.9-1.2 3-.3 3-.3c.2-.2-.4-1-1.1-1.1s-2.1-.2-2.1-.2.8-.5 2.2-.4 1.6.5 1.6.5z"/>
<path fill="#8f4620" d="m326.7 200.9-1.8.6 1 7.5 7.2 12.1 3.7-.6 1.3-2.3 1 .5c.4.4 1.8 2.8 3 3.3s1.7.2 2.7 1c1 1 4.9 6.6 5 6.8.3.2-.1-2.4-.1-2.4l-1.2-3.4-1.4-2.4-.3-.6h2l1.2.8 2 1.5.9 1.2.5 1.2.2 1 .5 1.6 1 2.3 1.3 2.1.4-.9-.6-2.5-2-6-1-2 1.9 1.2c.4.1 1.8 2.2 1.8 2.2l.9 2.2.6 2 .4 1.7.7 1.9 1-.7-.1-1.3-1-4.2-.4-4.5s.6 0 1.2.7l1.2 2.3.5 4.1.7 3.8 1.2 3.8 1.2 2.4.9 1.3v-2l-1.6-16 .2-.6s.2-.1.8.7c.5.7 1 1.9 1 1.9l.5 2.5.2 1.7.3 3.3.5 3.5 1.6 3.8.9 2 .5-2.4-.8-17.1.4-.5s.9.7 1.2 1.3l.8 1 .6 3.8.4 10.7.4 2.8 3.2-28.2 2-.4.2-.6-1.5-1-3.2-5-2.1-4.9-3.8-7-6.6-8-5.2-4.3-1.9-1.3.4-1 1-1.7-.1-.1h-.9l-2.2.4-2.2-.7-4.4-3-4.6-2.3-2.8-1-6.6-1.6-6.6-1h-3l-2.6.5s-1.4.6-1.8 1.5-.5 1.4-.4 2.6c.2 1.3 1 1.9 2.8 2.7 1.8.7 6.2 3.6 6.2 3.6l2 2.1.8 1 .2.8 1.4 11.1-2.8 6z"/>
<path fill="#ab6d29" d="M333.8 218.2s1.2 2 2.9.5c1.7-1.6-.8-6-.7-6.3 0-.3.7-.3.7-.3l1.3 2.2s.7 1.3.4 3.2-2.1 3.7-2.1 3.7l-2.2-.5z"/>
<path fill="#4d2a15" d="m375.3 244.2-1.6-4.5-.4-3.5c0-.6-.3-7.9-.5-9.3 0-1.4 0-3-.2-3.1l-1.6-2.2-.7-.7.5-.3s.6.6.7.3q.3-.7-.6-4.5c-.7-2.5-3.9-8.3-3.9-8.3l.4.2.9 1 1.8 1.5 4.1 1.5 2.2.3 1.4 2.9s1 4.9 1 7.4q.2 3.4-1.2 7c-1 2.4-1.2 10.3-1.3 11.5-.1 1.3-.6 2.5-.6 2.5l-.5.3z"/>
<path fill="#8f4620" d="M375.3 231.2s.8-1 1.2-2.4.5-12.4-.8-13.7a10 10 0 0 0-4-2.3c-.2 0 1 3.5 1 3.5l.8 1.4.6 1.8c.1 1 .9 3.1 1 6z"/>
<path fill="#4d2a15" d="M369.3 238s.5-5.1.3-9c-.3-3.7-.6-4.2-1-4.9a32 32 0 0 1-2.5-6.3c-.9-3.2-1.5-5.6-2-6.2s-1.7-2-2-1.6-.3 1.1-.3 1.1l-1.5-.4-.4-1 .5-1.6v-.7l-.3-1.1 2 1.6 4.2 2.8 2 8.6 2 5.3.2 9-.2 8-1-3.5zm2.4-24.7s1 1 1.7 1.4 1.8 1.1 1.6 1.2l-2-.8-1.2-.8v-1z"/>
<path fill="#4d2a15" d="M363.7 236s.8-1.8.7-4.1a49 49 0 0 0-1.6-8.6c-.4-.7-2-1.7-3.5-4.8s-1.3-3.8-2.3-5c-1-1-2-2.4-2-2.4l.3-3.3 3.6 1.8 1.6.8 1.8 8.7 1.6 2.5.7 3.5.9 8 .4 6.6-.2.5zm-4.2-3.7s.1-2.5-.5-4.8q-.9-3.5-1.9-4.8c-.7-.7-2.5-2.6-4.1-5-1.6-2.5-3.9-5-3.9-5v-3.3l2 1.5 2.4 1.2 1 3.7 1.2 2.7 1.8 2.4 1.2 1.1.7 4 .4 2.3 1 3.2-.4 1.7zm-3 0-1-.9s.6-.6.3-2q-.4-1.8-1.2-3.7-.7-1.8-1-2.3l-4.5-4c-1-1-2.7-2.6-3.7-3.3q-1.7-.8-1.8-.8l-.5-4s1.3 1.8 2.1 2l2.1.8 2.8 5 1 1.3 2.6 2 1 2.2.9 2.5 1 3.4.1 2zm-5.5-1s-1.7-2-2-3.2c-.2-1.2.2-1.2-.3-2.6s-2.3-3.5-3.7-4.6c-1.4-1.2-3.4-2.8-3.3-3.8q.3-1.2.4-1l.8.8 4.5 4.7 1.4 2.5 1 3.1.7 1.5.7 1.8zm16-23.3s3.1 3.8 4 .3c.3-1.6-1.2-4.6-1.2-4.6s-2.8-5-3.8-6.2l-5.7-7.3-3.5-1.6 1.2 2s2.6 2.6 5.9 8.4c3.3 5.7 2.7 8.9 2.7 8.9zm-17.8-23s6.1 5.2 10 10.8 5.5 9.4 4.7 10.2c-1 .7-3.4-.4-4.4-2s-1.8-4.4-5.5-8.6-5.5-4.8-5.5-4.8l-2.7-4.3zm-3.2 12.5s3.6 3 6 5.4l3.4 4.2 1.4 1.4s-.9-1.5-.4-2 1.1-.3 1.1-.3l-11.5-12zm-.7.3s1.2 2.3 3 4.1l4.1 4 .8 2.9s-5.2-4-5.2-3 1.2 3 1.2 3l-1.6-1-2.2-3.1-1.3-2.8-1.5-3.8z"/>
<path fill="#4d2a15" d="M329.5 210.2s2.7 2.4 4.2 0c1.5-2.3.3-5 .3-5s3.9 5.6 5 .3c.2-1-2.4-4.6-2.4-4.6l1-.9 1.9 3.3s1.8-.8 1.8-3-1.9-5.3-1.9-5.3l1.8 2s3.6 0 3.7-2c0-2.2-3-4.5-3-4.5s2.4.1 2.4-1.3-3.4-2.4-3.4-3.3 1-2.4 2.5-1.5 3.5 1.4 4.4-.2c.8-1.6-.1-2-.1-2l-2.1-.4-3.4-1.8-2.7-1-2.8-.4 1 2 .8 5.8v2.6l.5 5.8-1.4 4.7-.7.9-.5.6-1.2 2.6-1.6 1.8-2.2 2.7z"/>
<path fill="#ab6d29" d="M348.8 220.9s-.7-1.7-2.2-2.9a8 8 0 0 0-3.8-1.2l4 4.2zm5.6 1s-1.3-2.6-2.8-4.2-2.5-3.6-3-3.6h-.6l.4 3.3 2.7 2.7s2.6 2 3.3 1.8m4.6-.2s-.6-3-2.2-6c-1.5-2.8-3.1-4-3.1-4l.4 3 1.2 3 2 3zm3.8-1.7s1-6.2 0-8c-.9-1.6-2.7-2-2.7-2l-.6 1 .6 4.3 1.5 3.4zm3.7-10.3s.9.4 2 2.5a40 40 0 0 1 2.6 8.6c0 .3-.5.2-.7.2-.2-.1-3.9-6-3.9-6l-.6-5.4z"/>
<path fill="#d2a567" d="M340.4 216.8s2-.4 2-1c0-.8-2.4-2.2-2.2-3.3 0-.5 2 .1 2.1-.3s-2-3.6-1.3-4.4 2.4 3.5 2.4 3.5 1 2 .7 3.2c-1.2 3.6-3.7 2.3-3.7 2.3m4.6-4.4c.1-.3 3.1.4 3.1.2.2-.7-2.7-3-2.7-3s-.6-1.2-.4-1.4 2 .2 2 0c.2-.3-2-3.2-2-3.7s.6 0 .6 0 1.7 1.7 2.6 3.2c1 1.4 1.8 2.6 1.5 4.9-.4 2.5-5 .6-4.7-.2m13.6-4.6s-.5-2-.2-2.3l1.1-.5s-1.6-1-2.4-1q-.9-.2-1 0c-.2.4 1.3 2.4 1.3 2.4z"/>
<path fill="#d2a567" d="M349.4 209.4s4.7 1.2 4.9.8-3.7-4-3.7-4-.3-.6-.2-.8 1.7-.3 2.2.1 0 1.2.1 1.5c.2.3 1.7 2.5 2 2.1.2-.3-.4-3-.3-3.3s1.8 1.6 2.3 1.9 3 2.3 3.3 1.4c.3-1 .4-1.1.3-1.7 0-.6.9 2.6-.3 3s-2.6-.4-2.6-.4l-1.1-.8-.6-.7s.7 2.5-.2 2.7-1.7.2-1.7.2l-2-.2zm10.7-3.2s5 3 5.4 2.5-1.2-7.5-1-8.2c.3-.6 4 7.9 2.2 9-1.4 1-2 .3-2 .3l-2.5-1.3zm13.8 12.6 1 .5.7.2-1.4.2-.3-1zm-4.8-16.4.7.3.7.3s-.4.3-.6.2h-.4zm-1.4-2.5s.5.3 1.1-.2q.9-.7.6-1c0-.2-1-.2-1.4-.5q-.4-.4-.3-.6h-1.1l-.4.1zm4.3 14.4s1.5 1 2 1 1.1.5 1 .6c0 .1-1.2.2-1.8-.1q-.8-.3-.7-.1zm-8.1-22.5h.6q.5-.1.6 0c.2.2 0 1.2-.3 1.5l-1 .5-1.6-2.2zm-7.1-6.4h1.5q.8 0 1.5.6c.4.3.9 3.3.6 3.8-.2.6-3.6-2-3.7-2.1 0-.3-1.5-2.5-1.5-2.5zm-3.2-1.4s2.5.2 2.2-1.3a3 3 0 0 0-1.3-2.3l-2.9 2.1zm-6.5-2.8s.7.4 1.4 0c.7-.2 2.6-2.7 4-3s1.7-.3 2 0c.2.4 0 1.8-.2 2.2s-4.4 2-4.4 2l-1.5-.1-1.3-.5zm-.7-4.5c-.7-1.4-1.7-2-2.2-1.9-.4.1 1.4 1.5 1.7 3.5q.3 3.1.2 3.4l.8.1.2-.7.1-.5v-.5l-.1-1-.2-.9-.2-.6zm-5.8-2a15 15 0 0 0-5.5-3.7c-.2 0 4 2.5 5 4 .9 1.7.9 3.9 1.3 4.2s.8-.2.9-.2.3-.5.2-.7l-.2-1-.7-1-.4-.8zm-5.5-2.3c-2.4-2-6.6-3-10-3-3.5-.2-5.3.2-4.8 2s2 2.8 5 2.8c2.9 0 3.6-.3 5.2.2a10 10 0 0 1 3.6 2c.5.5-1.1-1.5-2.6-2.6q-2.4-1.7-2.5-1.7c-.1 0-.3-.7 1.7-.4s4.7 1.6 5.5 3.3.6 2.6.6 3 .7-.2.7-.2l.3-.9s0-.6-.3-1.4c-.4-.7-.5-1-1.4-2-.9-1.1-1-1-1-1zm-9.6 28.5c1.5 0 4.8-2.8 5.2-6.7s-1.9-11.9-2.5-12.7c-.5-.9 1-.4 1.7.4a25 25 0 0 1 2.5 10.6c0 4.2-.8 5.3-3 7.5-2.2 2-4 1.8-4 1.8zm5.1 11.6s.7 1 1.6.6c1.5-.5 3.8-1.7 3.8-3.8 0-1.4-1.8-2.7-1.9-3.2v-1l1.6.4.5 2.2s.6.7 1.3.4c.6-.2 2.5-1.6 2.2-3-.3-1.2-2-2.9-2.2-3.5q-.1-1.1 0-1.2l1.6.2.4 2s.4.6 1.3.4 2.4-1.7 2-2.5l-1.5-2.6v-.9l.5.4s.6.2 1.8-.1c1.1-.4 2.3-1.2 2.3-2s-1-1.6-1-2v-.6h.8l1 1.2.1 1.4-.3 1.2-1.4 1.6-1.4.6h-.4l.8 1.4-.2 2-1.3 1.2-1.7 1-.1 1.9-1 2-2 1.2h-.7l-.2 1.2-1.4 2-2.3 1.5-1.7.3-1-1.4z"/>
<path fill="#d2a567" d="M343.4 191.4s4 1.1 3.8-1.4c0-1.3-3.8-4.5-4-5s-.2-1.3-.2-1.3l1.9 1.8s2.5.6 3.2-.9c.7-1.4.1-2.3 0-2.3l.6-.1 1.1.2-.1 2.2-1.3 5.5s.3 2.2-1.4 2.3c-1.7.2-3.6-1-3.6-1m3.7 29.5s0-1.1-.8-2c-.9-.7-2.5-1.4-2.8-1.6l2.8 3.5zm5.6.2s-2-2.4-2.3-3.3c-.5-.8-1.5-2.6-2.3-2.7-.8 0 .8 3 .8 3l3.2 2.9zm5.4 0-2.1-4.2c-.4-.9-1.3-3.4-2.3-4.1s.1 1.1.1 1.1l1.1 3.3 1.9 2.9zm3.9-2.4s-.1-2.7-.5-4.6c-.3-1.8-1.7-3.4-1.8-3.3v3.3zm8.1 2-2-4.7c-.5-1.6-1.8-4.1-2-4.2l.4 3 1.7 4.7zm7-8.5s-1.2-1.2-3.9-2.3-5.4-1.2-5.4-1.2l.3.4 1.3 1 2.8 1.5 3.7.9 1.3-.3z"/>
<path fill="#202020" d="M348.2 213.8s0 2.4 1.3 3.8c1.2 1.5 4 4 4.6 4.6s1 3.4 1.9 5.8 1 4.4 1.4 5.1 1.1 1.8.5 1.7q-.8-.2-1.4-1.7c-.3-.9-.2-.9-.2-2 0-1.3-1.8-7.5-2.7-8.1-.9-.7-4.8-4-6.4-6.3-1.5-2.3-1.2-2.8-1.2-2.8z"/>
<path fill="#202020" d="M354.6 229.3q-1-2.7-1.2-4.3a16 16 0 0 0-4.4-4.2c-.6-.3-1.9 0-2.4-.3l-3.7-4s-.5.5-.3.6c.1.1 3.4 3.7 4.6 4.8 1.2 1.2 1.9 3.7 2.7 5.7s.5 1.6.6 2.5q.3 1.5 1.3 1.5t.6-.4a10 10 0 0 1-2.2-3.5c-.7-1.7-1.5-4.9-2.5-6l-.3-.3 1.3-.2c.8 0 4.1 3 4.4 4 .3.8 0 1.2 1.4 4.2s2 3.7 2 3.7h.2s-1.2-2-2-3.8zm-5.2-19.9s2.5 1.5 3.2 1.7l1.2.3s.4 4.4 2.1 6.8c1.8 2.5 3.2 3.4 3.2 3.4s0 4.8 1.4 8 1.5 7.3 1.2 8c-.3.6-1.4-2-2-4.3l-2-6.2a13 13 0 0 0-2.5-4.4c-.8-.7-.2-.6.3-.2s1.7 2 2.5 4.5 1.1 5 1.5 5q.8 0 .8-.9c0-.4-.6-1.8-1-4.1a17 17 0 0 0-1.2-4.7c-.4-.6-1-.7-2.4-3.4a20 20 0 0 1-2.5-5.3c-.2-1.2 0-1-.2-1.3-.2-.1-.4 0-1.6-.6-1.1-.6-2-2.3-2-2.3"/>
<path fill="#202020" d="M355.7 208.5s1.4 1.2 2.3 1.5 2 .4 2 .4-.3 4.2 1.3 6.9 3.3 4.2 3.3 4.2.3 1 .8 6.8l.7 8.3c.1 2.3.2 10.6-.1 10.3-1.3-1-.2-5.4-.9-7.3s-1.6-2.1-2.8-7.4c-1.2-5.2-.4-6.4-1.3-8.3s-2-2.2-2-2.2 1.6.4 2.3 2 .7 6 1.3 8.2c1.2 4.9 2.9 7.4 2.9 7.4s-.4-6.2-.8-9.7-.5-6.4-1.1-7.7c-.7-1.4-2.1-2.4-3-4.2s-1.1-5.3-1.5-6.2-.6-.6-1.2-.8c-2-.8-2.2-2.2-2.2-2.2"/>
<path fill="#202020" d="M360.1 206.2s1.3 1.5 3.1 2.6c1.8 1 2.8.7 3 1 .3.2-.3 3.8 1.6 7.2s2.6 3.5 2.6 4l.6 13.9c.3 3.4.5 14.8-.9 14.8s-.1-8.5-.8-10.4-.7-1.4-1.3-3-.7-7.8-1.8-11.5c-.8-2.8-1.6-3.2-1.6-3.2s1.4.7 2 3 .8 10 1.5 11.1c.8 1.3 1.8 2.8 2 4a157 157 0 0 0-.4-15.6c-.3-1-3.5-6.6-3.7-9.7-.3-3.2-.4-3.5-.7-3.7s-1.1 0-2.6-1.5c-1.5-1.4-2.6-3-2.6-3"/>
<path fill="#202020" d="M367.3 208c0-.3 1 1.7 3.7 2.8 2.9 1.2 5.6 1 6 1.3.3.3 2 4.5 2.1 10.2s-1 5.1-1.8 10-1 14.8-1.5 16.3c-.2.5-.5.8-.8 1.7-.3 1 .2 2.8-.7 2.8s-1-1.4-1-2.1 1-3.5 1-6.2-.6-3-1-6.2-.4-13.5-.8-14.5c-.3-1-2.1-3.1-2.1-3.1s1.9 1.2 2.4 2.6c.4 1.3.5 12.7 1 15s.3-.5.5-1.8.9-7 .8-10-1.4-9.4-2.4-10.5q.1 0 1.1 2 .1.3.8.7l1 .5s-.6 0-1-.3q-.6-.3-.6-.4a24 24 0 0 1 1.3 8c.1 5.2-.7 16.4.1 16.5s.7-6 1.5-11.2c.8-5 2-5.8 1.6-9.9s-1.4-8.6-2.3-9.2-3.4-.3-5.6-1.8c-2.2-1.4-3.4-2.9-3.3-3.2m-35.8 5.7s.8.4 1.7 0c1-.5 3.5-2.5 3.5-3.3v-1.6s0 .3.7.4c.8.1 2.8-1.4 3-2.4a8 8 0 0 0-.1-3.1c-.2-.4.6.3 1 0 .4-.4 2.4-1.7 2.4-3s-1.1-2.6-1.1-2.6 1 .2 1.6 0c.5-.1 2.5-1.3 2.4-2.4 0-1.2-.7-3-2.3-3.2 0 0 1.2.3 2.3-.1q1.5-.5 1.5-2.3c0-1.2-2.3-4-2.6-4.2s2.4.8 3-.3q1.1-1.6.7-2.5c-.2-.7-.8-.8-.8-.8s1.2.3 2.8-.5 3-2 3.8-1.9c.8 0-2.3 1.7-2.5 2.6q.1.6 2.3 2 0 .1.2.3c.7.3 3 .4 3.3.5.2.2-2.4.1-2.2.3a34 34 0 0 1 5.7 5.4q.2.2 1 .5l1.3.3h-1.6l3.3 4.7q0 .3.9.8.9.2 1.1.2c.1.2-1.4 0-1.3.2l3.3 6s-3-5.3-6.7-9.5-4.7-4.7-4.8-4.4c-.2.4 2.7 4.1 4.3 6.7s3 5.9 3 5.9-3.7-7.8-9-12.6-5.8-5.5-6.3-5.4c-.6.2-1 3.8-.7 4.5s4.2 3.6 7.4 7.5c3.3 3.8 7.5 10.4 7.5 10.4s-3-4.7-8-10-6.9-6.6-7.3-6.5-.4 1-1 1.8c-.6.7-1.8.6-1.8 1s4.2 4.3 5 4.7c.9.5 1.4.3 2 .6l1.4 1-1.6-.5q-1.1-.4-1.3 0a21 21 0 0 0 4 4.6c.6.3 1 .1 1.7.4.8.3 2 1.1 2 1.1s-1.3-.7-2.1-.8q-1.3-.2-1.2.2c.1.4 2.6 3.6 2.5 3.6s-4-4.6-7.3-7.5-4.8-3.6-5-3.5 0 1-.9 1.3q-1.4.3-1.5.7c0 .3 3 3 5 5.4l4 4.5-4.5-4.3q-3.4-3.1-3.8-3c-.2.2-.2 1.3 0 1.6s4.6 5.4 4.4 5.5c-.1.2-5-6-5.4-6-.3 0-.5.7-.8 1s-1.8.4-1.8 1c0 .4 7 7.3 6.9 7.5 0 0-5.8-5-6-4.7s2.4 5 2.2 5.2c-.1 0-3.4-5-3.7-5s-.2.6-1 1.3c-1 .8-2 .4-2 .8 0 .3 4.8 5.7 4.7 5.8-.2.1-5-5.1-5-4.8q-.3 0-.2.7c.3 1.3 1.4 3.6 1.3 3.8 0 0-1.6-3.2-2-3-.4 0-.4.7-1.2 1.1s-1.3.4-1.3.7.8 1 1.2 1.7.7 2.4.7 2.4-.5-1.4-1.1-2.3q-1-1.4-1.9-1.5c-.9-.1-.8.5-.8.5z"/>
<path fill="#202020" d="M377 212s1.5 0 1.7-.4c.2-.3-.6 0-2-1.6s-5-12.1-9-17.4c-6.6-8.7-12.7-12.7-12.7-12.7s7.3 4.2 13 12.4c5.6 8.4 7 14.9 8.6 16.6s3.2 2 3 2.5q0 .7-.9 1l-2 .1zm-48-2.2.3.1c.3 0 2.3-1.9 2.2-2.5 0 0-1-.3-1.6-1-.7-.8-.9-1.9-.9-1.9s.7 1.3 1.2 1.6q1 .7 1.8.3c.3-.2 3.2-2.4 3-3.2 0 0-.9 0-1.8-.6s-1-2-1-2 .6 1.1 1.5 1.5q1.4.5 2 0c.2-.3 2-3 1.8-3.7 0 0-1 0-1.8-.5s-1.3-2.2-1.3-2.2.6 1.3 1.7 1.5c1.1.3 2-.4 2.2-1.2s.8-3.7-.2-4.5c0 0-1.4.5-2.4 0s-1.4-1.9-1.4-1.9.9 1.2 2 1.4q1.7 0 1.9-1a6 6 0 0 0-.1-3c-.4-1-1.1-.8-1.3-2.2 0 0-.3.5-1.8.5s-2.3-1.6-2.3-1.6 1.2 1 2.4 1a2 2 0 0 0 1.6-1.9c0-.7-.6-1.4-1-2.1-.2-.8-.3-1.7-.5-1.6s-.5.8-2 .6-1.8-1.3-1.8-1.3 1 .7 2 .7q1.3-.1 1.3-1c0-.6-.3-1.7-2.2-3.3l-3.5-2.7a22 22 0 0 0 6.6 5.3s1.7.6 2 0q.3-.9-.5-2.4c-.5-1-2-2.6-2-2.6s1.6 1.3 2.3 2.7c.8 1.3.4 2.2.5 2.6s3.3 1.6 4 1.1q1-.8-.2-2.3l-1-1.8s.7.8 1.2 1.8q1 1.5.8 2.1-.4.7-.6.9l2.3 1.3c1 .6 2.3 1 2.5.8q.3-.6.2-2.2c-.1-1-.7-2.7-.7-2.7s.7 1.6.8 2.7-.1 2 .1 2.4 1.1.5 1.1.5-.6.4-2 0c-1.5-.4-7.7-3.6-8-3.3s2.9 2.3 4.3 3.7c1.5 1.3 2.2 2.8 2.2 2.8s-1.1-1-2.2-1.6-4.4-2.3-4.7-2 1.3.5 1.4.7c.1.3-1 1.3-1 1.8s4.7 3.7 4.5 4c-.1.4-1.2 1.3-1.4 1.2-.1 0-2.5-1.8-2.5-1.6s1.4 1.5 1.4 1.7c0 .3-1 .8-.8 1.4s2.6 2.6 2.6 3.3-1.4 1.3-1.1 1.7l1.5 2s-1.4-.9-1.8-1.4-1.3-1.5-1.5-1.3-1 2-1 2.5c.2.4 1.3 1 1.5 2.3.2 1.1-.2 2.4-.3 2.4s-1.6-2-2-2.1q-.4 0-.9.6c-.5.6-1.2 2.1-1 2.4 0 .3 1.6.7 1.6 1.9a3 3 0 0 1-1 2.3l-2.1-2.4c-.2 0-2.2 2.3-2.3 2.6 0 .2 1.4 1.3 1.2 1.7-.3.4-1.3-.2-1.8.1l-1.7.7c-.2 0-.5-.6-.5-.6"/>
<path fill="#202020" d="m355 180 .5-1c.3-.3 1.5-1.7 1-2-.4-.2-2 .3-3 .2s-1.7.2-4.6-2.3c0 0-9.3-6.4-23.6-7.3-4.6-.2-6.6 1.4-6.8 2.2 0 .2 1.2-1.8 5.7-1.7 2 0 16.5 1.1 24 7 .4.3 2.9 2.7 5.2 2.4 2.3-.2 2.8-.5 2.7-.2 0 .4-.7 1.2-.9 1.6l-.3 1.1zm-25 2v-.2c-.2-.4-1.1-1.7-2.7-3.1a37 37 0 0 0-8-4.3c-.3-.1 6 2.9 7.7 4.5z"/>
<path fill="#d2a567" d="M330 192.2s-.4-.6-1.7-1.4l-2.5-1.1 3 .7a5 5 0 0 1 1.1 1.8zm-.8-5.3s-.4-.6-2.1-1.2-2.2-.5-2.2-.5 1.6-.7 2.8 0a3 3 0 0 1 1.5 1.6zm-1-4.5s-.4-.8-2-1.3q-2.4-.8-2.6-.7c-.2.1 1.6-.4 3.2.2 1.5.6 1.4 1.8 1.4 1.8m-2-2.8s-.4-.6-3.3-1.3c-3-.7-4-.7-4-.7s4.1-.4 5.2.1 2.2 1.9 2.2 1.9zm-5.3-3s-1.8-.6-3.5-.7-4.4.1-4.4.1 5-.8 6.3-.5c1.4.3 1.6 1 1.6 1z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M304.5 238.4s.9 1.6 2.6 1.6c1.8.1 3.1 2.6 3.1 2.6l-1.9 1.9-4.6.2-1.5-2.4zm0-1.6c.2-.3-1.3-3.3-2.4-3.8s.4 1 .2 1.6-.4 1.4-.3 1.5c0 0 .9 1.4.4 2-.4.5 2.2-1.3 2.2-1.3z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M303.8 241.4s.8.4 1.4.2c.7-.2 1.3-.9 1.3-.9m-3.6-.5s2.6-1.9 2.4-2.9-.7-1-1-.9a7 7 0 0 0-2.1 1.7c-.1.3.7 2.1.7 2.1z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M301 241.5s.6 1.3 1.4 1.3 2-.7 2-1.2c.1-.5-.8-1.5-1.3-1.7-.6-.3-1.9.4-1.9.4z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M303.4 239.8c0 .4-1.7 1.2-2.3 1.2s-1.6-1.3-1.2-1.5l1.5-.4c.4-.2.5-.8.7-.8.3 0 1 .1 1.1.5q.4.6.2 1z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M301.8 240.9s-2.6 3.4-3.7 3.3c-1-.2.5-5 .5-5l1.2.6q1.4.3 2 1z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M297.2 238.4s3.3.6 3.4 1.1-1.5 1.3-2.5 1.3-.9-2.4-.9-2.4z"/>
<path fill="#202220" d="M289 243.4s.5 1.6 1.9 1.7 2.8-1.5 2.8-1.5l-3.4-1.3z"/>
<path fill="#4b4139" d="m289 243.4.4.9q.4.7 1.5.9h.1c1.4 0 2.7-1.6 2.7-1.6l-3.4-1.4zl1.3-1 3.3 1.3v-.1l-.2.2c-.4.4-1.4 1.2-2.4 1.2h-.1a2 2 0 0 1-1.4-.8l-.3-.6v-.1l-.1-.1z"/>
<path fill="#202220" d="M290 243s.5 1.2 2.4 1.2 3.3-1.5 3.3-1.5l-3-2.3z"/>
<path fill="#4b4139" d="M290 243s.5 1.2 2.4 1.3c2 0 3.4-1.5 3.4-1.5l-3-2.5-3 2.7zl2.8-2.5 2.9 2.3v-.1a5 5 0 0 1-3.2 1.4 3 3 0 0 1-2-.6l-.3-.4z"/>
<path fill="#4b4139" d="M290.2 243.3s.9.6 1.8.7 2.2-.3 2.2-.3l-1.1.4-.8.1-.8-.1-.6-.3zv-.2z"/>
<path fill="#202220" d="M292.4 241.6s.8 2 2.6 1.8c1.7-.2 1.7-1.2 1.7-1.2l-2-2.8z"/>
<path fill="#4b4139" d="m292.4 241.6.5 1q.6.8 1.8 1l.3-.1q1.2-.2 1.5-.7t.3-.6l-2-3zl2.4-2.1 1.9 2.7c0 .2-.3 1-1.8 1.1h-.2a2 2 0 0 1-1.7-.8l-.4-.6v-.2z"/>
<path fill="#4b4139" d="M292.6 242s1 1.3 2 1.3a3 3 0 0 0 1.5-.4v.1l-.5.3-.8.1h-.8l-.7-.5zm-3.2 2s.8.9 1.5.9a5 5 0 0 0 1.9-.6h.1l-.2.1-.8.4-.9.3-.7-.1-.6-.4-.3-.4z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M297 238.3c.8-.2 2.4 2 2.4 3.2s-.5 2.7-1.3 2.7-2.6-1.7-3-2.5q-.8-1.2-.5-1.9c.3-.4 2.4-1.5 2.4-1.5zm-8 .2s-1.4-1-1.9-.5-.3 2.3 0 2.6c.4.4 2-.8 2-.8z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M288.8 239.7s-1.8.2-1.8 1 .9 1.6 1 1.8c.2.1 1.8-.7 1.8-.7z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M289.5 241.1s-1.8.6-1.8 1.2.8 1.7 1.3 1.7 1-1 1.2-1.3c.3-.2-.7-1.6-.7-1.6zm.8-5.1s-.3-.5-1-.2c-.8.3-1.6 1-1.5 2.3.1 1.1.6 2 .6 2l2.7-1z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M290.5 238s-2.2.8-2.3 1.7q0 1.4 1 2c.7 0 2.6-1.8 2.6-1.8z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M292.5 241.7s-2.2 1.5-2.5 1.5-1.2-1.2-1-2 1.7-1.3 2.3-1.6c.6-.2 1.2 2.1 1.2 2.1zm2-8.2s-.7-.6-2-.5-2.4 2-2.6 2.8c-.2.9 0 2.6.5 2.8.5.3 3.3-2 3.3-2l.8-3z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M291.8 240.3c-1-.1-1.8-1.5-1.7-2.3s2-2.2 3-2c1 .1.8 2.5.8 2.5s-1.2 1.9-2.1 1.8z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M294.7 240s-.4 1.2-1.4 1.7q-1.4.5-2-.7c-.6-.8.2-1.2.6-1.6.5-.4 1.7-.7 1.7-.7l1.1 1.4z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M293.2 237.2s-1 .8-.4 1.8 1 1.4 1.8 1.5q1.1.2 1.9-.9c.6-.7 1.5-1 1.4-2q-.1-1.4-1-1.8c-.5-.3-2.2 0-3.7 1.4z"/>
<path fill="#202220" d="M296.2 233s-1-.5-1.9 0q-1.3.9-1.5 2.4l.2 2.3s1.2-.4 2-1q1-.8.8-1.1c0-.2-.4-.4-.4-1.2 0-1.4.8-1.5.8-1.5z"/>
<path fill="#4b4139" d="M296.2 233s-1.2-.3-1.9.1a3 3 0 0 0-1.3 1.9q-.3 1.1-.1 1v-1.4q.6-1.3 1.6-1.7c.8-.3 1.7 0 1.7 0z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="m328.8 257 1.6-.8 2-1.2.4 2.2-1.8 1.4h-1.5l-.9-.7.2-1z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M326.4 257.2h1c1.1 0 1.2-.3 1.4-.3.2.1.5 1.3.5 1.3l-.4.7-1.5.9-1.3-.8-.1-1.8zm12.1-.8s-.3-.5-.9-1l-2.2-1.9v5l2.3-.1 1.1-1.5z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M326.4 257.2s-.4-.5-1.3-.4-2 .9-2.1 1.5 0 2.6.5 3q.7.6 2.2.3c1-.3 6.4-3.6 6.4-3.6l-1.4.3c-.7.2-1.4-.1-1.4-.1s-.7.8-1.6 1q-1-.2-1.2-.9z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="m330 258.9-1.2.5-1.3.6.4 1.6 2.3.5 1.4-1.1v-1.6l-1.7-.5zm10.4-1.2-.8-.8q-.8-.5-1-.5l-1.3 1.5v1l1.2.7.9.2 1.1-.8z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M332.6 257s-1 .4-1.5.8l-1.2 1c0 .2 1.1 1.4 1.1 1.4l1.2.1 2.5-1.7-.2-1.6zm9.5 2-.5-.7q-.7-.6-1.2-.6c-.3 0-1.3 1.5-1.3 1.5v.4l.8 1 1.6.5 1.1-1.2z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-linecap="round" stroke-linejoin="round" stroke-width=".3" d="m338 260.8 1.2.2s0 .3.3.5.3 0 .3 0 .2 1.2 1.4 2c0 0 1.4.6 2 .2.8-.4.7 0 .7 0l2-2.4s0-.5-.5-.5c0 0 .4-1.6-1-2-1.2-.2-2.2.2-2.2.2s-1 1.8-1.6 1.6l-.8-.5-.7-.9s-.4.1-.9-.2-.9-1.1-.9-1.1-.7-.1-1-.4l-.5-.8-1.9 1.4s0 .5-1.1 1.3-1.7.8-1.7.8-.4 1.7-1.6 1.4-2-1.6-2-1.6-.9-.2-1.4.6c-.6.7-.9 1-.9 1.7 0 .8.5 1.8 1 2.2.3.3 1.5 1.3 3 .6 1.4-.8.9-2 .9-2l.8-.5c.9-.6 1.8-1.5 2.5-1.7"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-width=".3" d="M335.8 250.8v3.2s.4 2.9-.2 3.7c-.6 1-2.9.7-3.2 0-.3-.8-.2-3.2-.2-3.2z"/>
<path fill="#f8c83c" stroke="#977c2e" stroke-linecap="round" stroke-linejoin="round" stroke-width=".3" d="M333.3 260.5s-.5.8 1 1q.6 0 .9-.4.4-.7.3-.8c-.1-.1-.2 1.2 1.2 1.2 1.4.1 1.3-1.3 1.3-1.3"/>
<path fill="#202220" d="M321.2 263.1s-.8-1.4.1-1.9 1.5 0 2.1 0 .4-1.4.3-1.7l-.7-1.2c-.2-.2-3.5.2-3.4 2.3 0 2.1 1.6 2.5 1.6 2.5m5 5.2s-1-1.5-.4-2.4 1-.3 1.2-.4q.5 0 0-1.6-.9-1.4-1.4-1.5c-.4 0-2.3 1.4-2 3.2a4 4 0 0 0 2.6 2.7m18.2-1.8s3.5 1.5 3.7-2c.2-2-1.5-3.3-2-3.3q-.5 0-1.3 1-1.1 1.2-.9 1.6c.3.3 1.3-.3 1.8.8.7 2-1.3 1.9-1.3 1.9"/>
<path fill="#4b4139" d="M326.2 268.3s-2.7-.6-2.7-3.3c0-1.8 2-2.6 2-2.6s-1.8 1-1.8 2.6c-.1 2.7 2.5 3.3 2.5 3.3m-5-5.2s-1.6-.3-1.7-2.6c-.2-2.3 3.5-2.2 3.5-2.2s-3.5.2-3.4 2.4 1.6 2.4 1.6 2.4m23.2 3.4s.7.3 1.5.3 1.8-.3 2-1.6c.4-1.3.3-2-.6-3.1-.6-.9-1.2-.8-1.2-.8s.6 0 1 .8c.6.8 1.3 1.6.8 3.1s-2 1.5-2.5 1.5z"/>
<path fill="#904720" d="m302.6 192.4-.6-.2.4-1 1.9-.7 1.8-.1 2-.1.8.6 1 2.3-3.6 2.2-.6.2-.2-1-.6-1.4z"/>
<path fill="#202020" d="m337 245.5-.3-5.5-14.8 4.6 5.8 5.5z"/>
<path fill="#d2a567" d="m309.4 235.9-1.2-1-1.4-.8h-1.1l-.2.3.2.5 1.3 1.6 2.3 1.2.8.7.5.2-1.1-2.7zm-.3 2.3.8.2.9 1 .5 1 .7 1.3.2.8-1.6-.3-4.8-1.5.4-1.2.8-.7.7-.3 1-.2z"/>
<path fill="#d2a567" d="M312.2 242.2h.8l.6-.5-.3-1.8.3-5.8-3.9-4.6-.8 2.6 1.1 4.7.9 2.8z"/>
<path fill="#ab6d29" d="M309.4 231.7s-.2.4.1 2c.3 1.5.9 4.9 2.2 7.2l-1.3-1.9s-2-4.6-1.7-8.2.7.8.7.8zm2.9 2.9s-.9 1-1 1.9v2.2s0-1 .8-1.3.7 2.3.8 2.6l.5-4.7zm.4 9.6-4.4-1q-3.5-1.5-3.4-2.3c0-.6 4 .1 4.6.2l2.4 1h1l1.9-.8.2 2zm-7.3-.8s1 .8.5 1a4 4 0 0 1-2.3.3l4.2 1 .9-.7z"/>
<path fill="#d2a567" d="M304.2 242.2c.2-.2.3.7 1.8.8s2.2-.2 2.2-.2l.7.4.8.2-.7 1.1-2.8-.8-1.5-.8z"/>
<path fill="#ab6d29" d="m314 240.3.8 1 .5-2.4v-2.5l-1.6-1z"/>
<path fill="#4d2a15" d="m309.7 236.1-1.3-2.1-1.7-3.1 1-1.8 1.3 1.8s-.4 1.3-.1 2.5c.2 1.2.8 2.7.8 2.7"/>
<path fill="#d2a567" d="M300.8 244s.6-.9 1.9-1l2.4.2 2.6 1.4.6.9-5-.7z"/>
<path fill="#8f4620" d="m314 235 .2 4c0 1 .7 2 .6 2.3q-.2.5-1 .9c-.6.2-2 0-2 0s1.3-.2 1.4-.5-.4-1.4-.5-2.4.1-2.2-.3-2.2q-.5.2-.8.6s.4-3.8.8-4.2 1.6 1.6 1.6 1.6z"/>
<path fill="#ab6d29" d="M304.7 243s-.5-.4-.5-.8c0-.3.6 1.6 3.4 1.5 2.8 0 2.6-.2 2.6-.2h1.3s-.3 1.3-.5 1.4l-2.2 1.4-.7.4v-.2l-.3-2.1-2.4-1z"/>
<path fill="#8f4620" d="M309.7 236.1s-3.2-3.3-4.3-5.5-.4-5.2-.4-5.2l3 4s-.4.7-.3 2c.2 1.2 2 4.7 2 4.7"/>
<path fill="#ab6d29" d="M307.6 248.3c0-.3-.2-1 .4-1.8 2-2.4 6.8-4 6.8-4v2.8l-6.2 3z"/>
<path fill="#8f4620" d="M308 246c.3-.1 1.4-1 1.4-1.3q0-.4-2.1-.7c-1.5-.3-2.6-1-2.6-1s1.7 1.4 2.4 1.7q.9.3.5.7zm8.3.7s-.9-1.3-1.2-2.1-.3-2.1-.3-2.2l-1 .7-2 .7s2 0 2 .6-3 1-3 1c0 .1 2.4-.4 2.3.1s-1.2.7-2.5 1.2l-2.3 1-.7.5s.6.4 1.5.3c.9 0 2.9-.5 2.9-.5l3.8-1.5z"/>
<path fill="#ab6d29" d="M316.3 246.7s-1-.3-1.8-4.8-.6-5.7-.6-5.7l1.8 1.3 1.7 3z"/>
<path fill="#8f4620" d="M316.9 239.3s-.9 2.5-.9 4q-.2 2 .3 3.4c.5 1.4 1.8 2.3 1.8 2.3l.7-.6.3-2 .1-3.4z"/>
<path fill="#202020" d="M319.4 242.7s.4 6.3-1.2 6.5c-.7 0-1.9-2.4-1.9-2.5 0 0 1.4 2.3 2 2.1 1-.4.6-6.6.6-6.6zm-3.1 4s-.3 0-.7-.8c-.4-.7 0 .6-3.4 1.8s-4.6.5-4.6.5 1.1 1 4.5 0 3.1-1.3 3.6-1.4z"/>
<path fill="#ab6d29" d="M310 238.7s.6.5 1.2 1.7l1 2h.2s-.4-1.3-1-2.2l-2.2-3.1z"/>
<path fill="#8f4620" d="M308 246.5s.3-1.1 0-1.4-.7 0-3-.2l-4.2-.9s1.6 1 3 1.4l3.5.8.5.1z"/>
<path fill="#202020" d="m308 246.5-.2-.5c-.3-.3-.7.1-3.5-.7s-3.5-1.3-3.5-1.3 1 1 3.4 1.5z"/>
<path fill="#ab6d29" d="M309.3 238.2s-2.1.1-2.8.7-1.6 2-1.6 2l1.3.1s-.1-1.2.5-1.9q1.1-.7 2.6-.9"/>
<path fill="#8f4620" d="M305.3 234.2s.8 1.2 1.7 1.5q1.5.6 2.2 1.2c.4.4.9 1.8.9 1.8s-3-1.3-3.7-2z"/>
<path fill="#8f4620" d="M308.2 235s-.3-.5-1.1-.9-1.4-.3-1.7-.1-.1.2-.1.2l1 1.7.7-.2s-1.6-1-1.3-1.3c.8-.7 2.5.5 2.5.5z"/>
<path fill="#202020" d="m316.3 231.1 17-8.7 8 11.1-14.7 4-7.7.6-3.8-2.7z"/>
<path fill="#4d2a15" d="m339 235.9 21 18.2-2.3 1.6-5-1.4-10.2-10.1-3.8-3.8z"/>
<path fill="#4d2a15" d="m366 250.5-12-9.1-15.3-11.5.4 5.5 17.4 16 5.7 2.7 3-.8 1.4-1.6z"/>
<path fill="#803f1d" d="M339 237.6s6 6.9 10.6 10.4 9 7.3 10 6.7c.8-.5.5-1 .5-1l-21.3-17.5z"/>
<path fill="#4d2a15" d="m353.8 255.1-2.1 1.7-2.5-1-13-13.2.7-2.5 1.8.3z"/>
<path fill="#202020" d="M366 250.5s-1.6 3.8-5 2.9-22-18.5-22-18.5v1.8s18.8 17.3 22.5 18 6.6-2.5 6.2-2.8z"/>
<path fill="#803f1d" d="M336.9 240.4s6 7.7 9.1 10.5c3.2 2.9 6 5 7.2 4.8s-.7-1.7-.7-1.7l-9.1-9-4.7-4.6s-1.8-.7-1.8 0"/>
<path fill="#202020" d="M338.4 240.4s15.2 15.5 16.4 15.9c2.5.7 6.2-2 6.2-2l-1.3-.7-.2.5s-.7 1.6-4 .9-16.6-15.1-16.6-15.1z"/>
<path fill="#4d2a15" d="m344 255.3-5.5-6.4-1.2-2.1-.3-2.7 11 11.1-1.5 1-1.4-.4z"/>
<path fill="#202020" d="M354.1 255.5s-1.3 2-3.2 1.6a5 5 0 0 1-2.8-1.7L337.5 245l-.3-1.6s11.7 12.3 13.7 12.8c1.6.4 2.7-1.4 2.7-1.4z"/>
<path fill="#202020" d="M348.2 255.1s-.5 1.6-2 1.4c-1.6-.3-2-.9-2-.9l-6.6-7.7-.1-2s6.8 9.3 8.3 9.8 2-1 2-1z"/>
<path fill="#b07229" d="M341.3 234.9s6.3 5.4 6.8 5.6c.5.3 2.7.4 2.8.7.2.3-1.4 0-1.5.3-.1.4 3.2 3 3.7 3.1.5 0 3.2 0 3.1.4 0 .3-1.8 0-1.9.4s2.9 2.7 3.4 2.8 3.9 0 3.9.4c0 .3-2.7 0-2.7.5.1.6 4.6 3.4 4.4 3.8s-5.4-3.5-5.5-3.3c-.2.2-.4 1.4-.7 1.6s0-1.6-.2-2.4-3.4-3.1-3.7-2.9 0 1.7-.4 1.7.1-1.7-.2-2.3a16 16 0 0 0-3.6-3c-.4 0-.1 1.3-.4 1.5s-.2-1.5-.6-2-7-6.5-6.7-7z"/>
<path fill="#4d2a15" d="M330.5 252.3s0 2.4-1 2.8c-1.1.3-3.1-4.4-3.1-4.4l-.5-4.4 3.1 3zm8.4-1.2V250c0-.6-.3-1.5-.3-1.5l-.2-2.4-.6-1.7-.4-1h-.7l-1.7 1.2-.5 1.6.8 3 .8 2.7 1.3.4 1.5-1z"/>
<path fill="#4d2a15" d="M336.8 255.3c.3-.1-.4-3.4-.4-3.4l-2-4.8-.5-1.7-1.5-.6-1 1.2.2 1.9s1.5 3.2 2.2 4.3 2.3 3.3 3 3.1"/>
<path fill="#202020" d="M335 245a6 6 0 0 1 1.4 2.6s1.3 3.6 1.1 4-1 .3-1 .3 1.8 1.8 2.3 1.6.3-2 .1-2.4-.7 0-1-.5c-.1-.4-.8-2.7-.8-4.2v-2.7c.2-.3 1.5 3 1.5 4.7l.6.7-.6-3.2-.7-2.3q.1-.4.3-1.5v-1s.6.5.9.5q.5.2.8-2c.2-1.5-.1-6-.1-6l-6.8 9z"/>
<path fill="#202020" d="M335 244.3s-.3 2.4.6 4.8l1.3 3c.2.3.2 2.9-.1 3.2 0 0-1.8-4.4-2.4-6.6q-.7-3-1-3.2l-.8-.2z"/>
<path fill="#4d2a15" d="M334.4 257c.2 0 0-1 0-1l-.2-2.3-2.4-4.9-2.6-2.3-1.4-2-.2 4s2 4 3.1 5c1.3 1.2 3.3 3.6 3.7 3.5"/>
<path fill="#202020" d="M324.7 246s.4 1.1.4 1.8v1.4l1.8 3.5c.9 1.5 1.7 3.1 2.4 2.8s1-1.5 1-1.7c.1-.1-.8 1.3-1.2 1.1a40 40 0 0 1-2.3-6.6l-1-.8-1.1-1.4zm7 5.5-1.7-3.1q-1-1.6-1.4-1.7c-.2 0-.5-2-.5-2s.7 1.3 1.6 1.7q1.4.8 2 .7l.2 2z"/>
<path fill="#5c3a1d" d="M327.4 248.3s.5 1.4 1.4 2.7 2.3 2.9 2.3 2.9-1.6-1.6-2.5-2.8l-1.4-2.8z"/>
<path fill="#202020" d="M332 246.7s.5-1.2.8-1.3l1.2-.3s-.7-.1-1.2-.8l-1.3-1.3.4 2zm-.4.4s2 4 2.4 5.5c.5 1.3.4 3.4.4 3.4s-.2-1.5-.6-2.7l-2.4-5c-.1-.3.2-1.2.2-1.2"/>
<path fill="#4d2a15" d="m334.2 244.4.5-6-.5-2.2-2.7-6s-.1.8-.8.8a6 6 0 0 1-3.1-1.8 9 9 0 0 0-.2 4.8l5.3 9.9z"/>
<path fill="#8b441f" d="M323.8 248.3s.8 1 1.3 1c.4-.2.3-2.1-.3-3.2-.6-1-7-9.6-8-10.1s7 12.3 7 12.3"/>
<path fill="#8b441f" stroke="#5c3a1d" stroke-width=".1" d="M328.1 244.8c.2.4.6 3.1-.5 3.4-1.5.3-8.5-9.4-9.6-10.7 0 0-4.4-2.8-4.5-3.5s4.7 1 4.7 1l9.5 9z"/>
<path fill="#202020" d="M323.8 248.3s-.8-1.3-.9-2.3q-.2-1.4-.4-1.9c0-.3-5.2-7.7-5.7-8.2s-2.3-1.4-2.3-1.4l.3 2.4s1.6 3 4 5.7z"/>
<path fill="#4d2a15" d="M331.6 247c.7-.3.4-2.7.1-3.5l-4.4-9.4-1.3.4-2.3-1.8-1.4-1.5s.5 2.1.4 3.1v1.6l3.3 5.9zm4.7-5.2 1-1.3-.5-4.4-2.8-9.4s-.7.1-1.4-.3-1.4-1.3-1.4-1.3l.7 5.7 2.9 8 .2 2.3z"/>
<path fill="#202020" d="M327.7 244s-2.8-3-3.7-4.5l-1.7-3s-.8.3-1.8 0c-1-.5-2.2-1.8-2.2-1.8s-.8 1.5 3.2 5.6c4.6 4.6 6.2 3.7 6.2 3.7"/>
<path fill="#8b441f" d="M334.4 245c1.2-.4 1.1-5.4 0-8.7-1-3.3-2.9-6.2-2.9-6.2s2.3 5.4 2.8 7.5c.4 2.2 0 3.7-.4 3.6s-.4-1.3-1.3-3.4q-1.6-2.8-1.6-2.6c0 .2 3.3 8.8 2.3 8.6s-5.3-9.9-5.5-10.2-.4.4-.4.4 4 12.2 7 11"/>
<path fill="#4d2a15" d="m337 221.3 2 4.2.6 4.4.2 3.7s0 7.4-.8 7.6c-1 .1-1.4-.7-1.4-.9l-1.7-8.3-1.7-8.5.6-.6.2-1.2 1.3-.1z"/>
<path fill="#8b441f" d="M331.6 247c.8 0 0-2 0-2s-.6 1-1.2.3-1.3-3.1-1.3-3.1l-.5-1.5s-.6 1.6-1.3 1.6-4.6-6.4-4.6-6.4l-.4.6s5.4 11.1 9.3 10.6z"/>
<path fill="#202020" d="M329.1 242.2s.7-1.6.4-3c-.3-1.5-2.2-5.1-2.2-5.1s-.6.1-1.4-.2l-2.2-1.2a38 38 0 0 0 5.4 9.5m3.1-2.3s-.5-3.4-2-6.6c-1.6-3.1-2.6-4.1-2.6-4.1v3.5s1.5.5 2.6 2.4c1 2 2 4.8 2 4.8m4.3-5.8s-.3-3.7-.8-5.8l-1.2-5s0-.2.3-.4h.6c.8.3 1.1 2.2 1.1 2.2l-.1-3.1-.3-.5h-1.4s.3 1-.1 1.3c-.3.3-.6-.2-.7-.3l-.2.4.4 2-.1 1.7s.5 2.2 1.2 3.5c.6 1.4 1.3 4 1.3 4m3.3-.5s.6-5.3-.6-8.7-2.2-4.2-2.2-4.2l-.6.6s1.8 3 2.4 5.6zm-5.4 2.7s-.6-4.1-1.3-6.8l-1-3.6-1.1-1s.7 2.7.6 3.6l-.1 1.4s.5 1.4 1.3 2.6z"/>
<path fill="#904720" d="M337.2 242.9c1 0 .1-6-.3-7.5-.4-1.6-3-8.8-3-8.8s3 10.3 2.8 10.8c-.3.8-1.5-3.1-1.7-3-.2 0 2.2 6.9 1.3 7-.8 0-1.3-1.5-1.3-1.5l.1 1.5s.6 1.4 2.1 1.5"/>
<path fill="#904720" d="M338.5 239.9c.7 0-.2-5.7-.7-8.2l-2-7.5s1.5 3.5 2.3 7.5 1.4 9 .6 9.2c-.8 0-1.2-.8-1.2-.8v-.9s.3.7 1 .7"/>
<path fill="#312317" d="M328.8 246.5c0 .4 0 2.4-1 2.2s-2.9-1.9-3.3-3c0 0 2.2 2.5 3 2.4.8 0 .8-2.5.6-3.2 0-.6.6 1.6.6 1.6z"/>
<path fill="#d2a567" d="M334.1 219.5s1.4.9 2.4.3a4 4 0 0 0 1.8-3q.2-1.1-.7-2.4c-.7-.9 1 .7 1 2.3s0 4.3-2.5 5c-2.5.5-1.4-.2-1.4-.2z"/>
<path fill="#8f4620" d="M332.1 214.7s1.6 2.6 2.2 5q.9 3.3.2 3.3t-.7-.9c0-.6-1.7-7.4-1.7-7.4"/>
<path fill="#1e2121" d="m304.1 218 10.6 15.3 13.2-15-8.3-8.6z"/>
<path fill="#4d2a15" d="m317.5 208.5-.6 2.4 1.3 4.1 2.2 2.3 2.5 1.7.6-2 .6-2.2.7-.2 1.6.6 1-1.5 1.8-3.1-.4-1.2-.4-.9-2.7-4.6-.9 1.7-.8.5-1.6-.8-1.8 2.4-2.3-1z"/>
<path fill="#5c3818" d="M323 219s.4-.3.6-1.6c.2-1.2.2-4.9-.1-6.4s-1.3-4.4-1.5-4.2 1.5 3.3 1.3 7c-.3 3.6-.2 4.6-1 5-.7.3.6.2.6.2z"/>
<path fill="#5c3818" d="M323.6 212.8s1.7 2.5 2.5 1.6.5-4 0-5.3l-1.2-3 .5-.6.9 3.4c.3 1.4 1.3 5.2.1 6-1.1.6-2.8-1.5-2.8-1.5z"/>
<path fill="#8f4620" d="M317.9 214.6s.6-1 1.4-1q1.1.2 1 .3l.6 2 1.2 2.2 1 1-.5.6-.9.2-2.5-1.4-.7-1-.6-3z"/>
<path fill="#d2a567" d="M322 236.3c-.8.5-2.7-.5-3.7-1.4l-6.8-6.7-.8-3.2 2.2.6 3.9-1.7.7-1.3 4.6-2.4 2.6-1.4 5.5-.1 1.4-2.7s1.8 3.2 2.1 4.5c.4 1.4.3 5.4-.2 5.7s-2.3-1.2-2.6-1.3c-.2-.1 1.4 5.7 0 6s-3.6-2.2-3.6-2.2 1 5.2-.2 5.2a10 10 0 0 1-5-2.6s1.3 4.3 0 5z"/>
<path fill="#8f4620" d="m321 229.6 1.1 1.7c.3.4 0 2.9-.2 3.4-.3.9-1.6-1.2-2.3-3l-2.2-3.8c0-.4 3.5 1.7 3.5 1.7zm10.8-12.9s2 8.1 1 8.4-3.6-2.6-4-3l-4-6.4-.8-.8.2-.4 1.7.6z"/>
<path fill="#8f4620" d="M317.9 232.3c-.4.3-1.3-1-1.7-1.4l-3-3.3a12 12 0 0 1-2.2-2.5c0-.3-.8-1.1-.8-1.1l.7-1h.9s5.4 4.2 5.7 5c.4.6.6 4.1.4 4.3m8 0c-.2.3-3.7-2.3-5.2-4.4s-3-4-3.5-5l-.6-1.9 1 .2s8.8 10.8 8.4 11.2zm1.3-3.7c-.2.3-1.7-.9-2.7-2.1s-.4-2.9-.4-2.9 3.3 4.8 3.1 5m2-.5c-.5.3-2.5-2.2-4.2-5-1.3-2-1.6-2.3-1.8-2.5-.5-.3-1.4-.4-1.4-.4l-.2-.3 1-.2.6-.8 1-1.3 4.3 8.5s1 1.9.7 2"/>
<path fill="#ab6d29" d="M318.3 234.9c0 .3 0 .6-1.2.5a21 21 0 0 1-9.9-7.8l-3-5 1.1-1.4 9.6 11z"/>
<path fill="#8f4620" d="M318.3 234.9q-.1.7-1.1.5c-.6-.2-3-1-6.9-5.3a33 33 0 0 1-5.3-7.4l-.2-1.8 1.2-.5 6.5 10z"/>
<path fill="#4d2a15" d="M324.6 230.6s-2-1.5-3.2-3a24 24 0 0 1-3.8-5.7c.1-.7 1.2-.7 1.2-.7v-1.8l.2-.6 1.9.8 2.3 2.9.9 1.1s1.5 3.7 1.2 4-2.4-2-2.7-1.7 2.4 4.4 2 4.7m-3.7-1s.7 1 .4 1.7-.5 1-1.5-.4a16 16 0 0 0-2.3-3c0 .1.1 2.3-.3 2.3s-2-1.9-3-3.3l-2.5-3.9v-2l-.1-2.5.3.9.4.7 3.6 4.2 1-.7 2.1 4zm-13.5-6.1s2.8 4.8 5.3 7.5a17 17 0 0 0 5.6 3.9l-8.4-9.9zm21.2 2.8a12 12 0 0 1-3-3.4c-1-1.6-1.9-4-1.8-5s.4-3.4.4-3.4 2.8 3 3.4 4.3c.5 1.4 1.7 3.3 1.4 3.5-.2.2-2.1-1.3-2.4-1s2.3 4.8 2 5m3.2-3c-.3.1-1.8-1.6-2.1-2.6l-.6-3.2-1.2-3v-3.3l1.4-.2 1.9 2.7s.7 1.5.7 3.7.3 5.6 0 5.8zm-22.5 8a42 42 0 0 1-4-6l-1-2.9.5.4s.8 3.2 4.7 7.1c4 4 4.9 4.2 4.9 4.2s.7 2.2.4 3c0 0-2.9-2.6-5.5-5.7z"/>
<path fill="#202020" d="M313.6 235.9s-3.4-3-6-6.4a47 47 0 0 1-4.4-6.6l1.2-1a46 46 0 0 0 9.2 14m4.7-1-6-6.1c-2-2.2-3-4.4-3-4.4l1.6.6s.7 1.7 2 3.5zm-.8-7s-2.1-2.6-3.1-4.2l-2-3.8s2.8 3.3 3.6 3.5.9 0 .9 0-.4 1.2-.1 2.2zm.9-10.6s.3 1.9.1 3c-.2 1-.8 1-.8 1a16 16 0 0 0 4 4l-1.8-2.7c-.5-1-1.3-2-1-2.8.4-1 2.1.6 2.6 1l2.6 2.8s-.8-2.5-1.8-3.4z"/>
<path fill="#202020" d="m327 222.8-1.7-4.2-1.6-5.2v2.8c-.1 1.2-.5 3-1 3.3s-.7.3-1.7 0 .7.5.7.5 1 0 1.4-.5.5-1.5 1-1.4 1 1.7 1.3 2.4z"/>
<path fill="#202020" d="M323.7 213.4s2 2.3 3 1.3c.8-1 .3-4 .3-4s1.2.5 1.6.2c.4-.4.2-1.5.2-1.5s2.3 3.2 3.3 5.3c1.1 2 1.6 5.8 1.6 5.8s-1.8-3-2.7-5.8c-.9-2.6-2.1-3.8-2.3-3.5a5 5 0 0 0-.3 3c.2 1 1 2.1 1.5 3.5l1.1 3.4-1.6-2.5q-1-1.1-1.7-1.6c-.4-.4-1-1.2-1.7-1.4-.6-.3-1.8-.9-1.8-.4s-.3-1-.3-1z"/>
<path fill="#171717" d="M316.8 223.3a27 27 0 0 0 4.2 6.3s-2-1.8-2.8-3a16 16 0 0 1-1.4-3.3"/>
<path fill="#8f4620" d="M330.2 223.9c-.3 0-1.3-.3-1.4 0 0 .4.6 1.8 1.3 2.5q1 .9 1.1.6.2-.2-.2-1.4v-1.7c-.1-.3-.8 0-.8 0"/>
<path fill="#d2a567" d="m306 209.4-1.3.7-1.4 3 .4 5.3 2.1 3c.8 1.2 3.5 4.2 4.2 4s1.4-2.3 1.5-3.4l.2-3.2 1.8 2.3c1.1 1.3 2 2.4 2.6 2.3.5 0 2-3.2 2-3.6.1-.4-3.8-10-3.8-10l-1.8-1-6.6.6z"/>
<path fill="#ab6d29" d="m314.8 217.4 1 3.7c.5 1.2 0 2.3.3 2.3s2.3-2.7 2.3-4 0-2.5-.5-6.3.2-5 .2-5l-1.2-1-2 4.2zm-7-2.2.7 5c.3 1.2 1 3.3 1.4 3.2s1.6-3.3 1.7-4.4c0 0-.9-1.6-.8-5.5 0-4-.4-4.4-.4-4.4z"/>
<path fill="#45392d" d="M302.5 207.5s-3.1 4.6-3 7c0 2.5.4 2 1.1 4.2s1.1 5.6 1.2 5.9c0 .2.5-.2.5-.5z"/>
<path fill="#ab6d29" d="M302.5 208s-2.7 4.3-2.5 6.6.5 2.3 1.3 4.4c.8 2 .8 5.4.8 5.4s1-.2 1.9-.9c1-.7 1.8-2.1 1.8-2.1l-1-2.1-.6-2-.8-1 .2-4.1.7-3v-1l-1.8-.1z"/>
<path fill="#dbad6c" d="M317.2 215.3s-1.3-.3-1.9-.6-.8-1.1-.8-1.1l.4-.8z"/>
<path fill="#d2a567" d="M303 208.5s-2 4.3-1.7 7.2 1.9 6.6 2 6.7q.1 0 .1-1v-8.7l.9-2.8v-1.5z"/>
<path fill="#8f4620" d="M307.8 209.5s.3 1.2.1 1.8c-.2.5-1.2 1.2-1.6 2s-1 2.8-1 2.8.4-1.1 1-1.8q1-1.2 1-.8c.2.4.4 2 .3 2.3q-.4.3-1 2c-.4 1.3 0 2.4 0 2.4l.5-2.2c.2-.6.7-1 .7-1l1 2.4s0-1.1.3-2c.1-.8 1-2 1.3-2s.8-.1-.3-.7c-.4-.2-.1-4.2.1-4.4s1.9-.2 1.7-.8l-1.6-2.2c-.1-.1-2.5 2.2-2.5 2.2"/>
<path fill="#dbad6c" d="m317 218.1-1.5-.3c-.5 0-.6-.6-.6-.6l.2-.6 1.2.7z"/>
<path fill="#8f4620" d="M304.4 218.6q-.1.6-.5 1.5c-.3.7-.4 1.6-.4 1.6l-.4-3.3-.3-2.4c-.2 0-.7.7-.7.7s0-1.6.3-2.3.5-1.8.3-2q-.6 0-1 .7l-.8 1.3s.3-1.3.6-1.9 1.8-2.2 2-2.8q.5-1.2.4-1.4h1s1 .8 1 1.5c0 .4-1 .9-1 1s-.8-.1-1 3.2.6 3.2.5 4.6m9.4-8.7s.2 1.8 0 2.4q-.4.8-1.1 1.8c-.4.7-.4 2.4-.4 2.4l.5-1.4.7-1q.3.1.8 1.7l.8 2.5s-.2-1.2.1-1.2.5-.1.9.2l.9.8s0-.5-.7-1.1-1-.6-1.1-1.5q-.3-1.6-.2-2.2c0-.3.5.3.9.7l1.5 1.4-.7-1v-3q-.1-1.7.4-2.1c.3-.3.4-1.6.2-1.8-.1-.2-3.5 2.4-3.5 2.4"/>
<path fill="#1e2121" d="M324.6 206.1a24 24 0 0 0 1.5 7.1s-1-2-1.6-3.6-.9-3.5-1.3-3.6-1 .8-1 1.5.5 3.3 1.1 4.2-1-1.2-1.3-2.3l-.7-2.2s-.4 1-.3 2c.2 1.1 1.1 2.7 1.1 2.7l-1-1-.6-.7s0 1.5.4 2.1l1.2 1.7-1.3-1-.4-.8s.2 2.1.6 3.3l1.5 3s-1.2-1-2-2.9-1.2-7.3-1-8.1l1.1-1.9c.6-.8 1.3-4.7 1.3-4.7s.9 3.6 1.5 4.3q1 1 1.2 1zm-8.6-1 .8 1.3.6 1s-1 1.1-2.3 2c-1.2 1-2.8 2-3.1 1.5q-.5-.9-.4-1c.1-.1 1.4-.8 2.4-1.8a7 7 0 0 0 1.6-2.2q.4-.9.5-.9zm-10.9 3.5s.2 2.2 1.2 2.2 3.7-2.9 3.9-3 .4-.8.4-1l-.2-.8s-3 3-4 3.2c-.9 0-1.3-.6-1.3-.6m.1-3-.3 2q.1.8.2.9c.1.1-2.2.7-2.5 0-.4-.6-.1-1.6-.1-1.6h1.5c.6-.2 1.2-1.3 1.2-1.3m20.3-3.1 1.5 3.2 1.4 2.8s-1.4-1.7-1.9-2l-1.2-1 .2-1.1z"/>
<path fill="#dbad6c" d="M302.1 207h1.3q.8-.1.8-.2l.9-1.2s-.5 1.4-.2 2.5c.3 1 1 1.1 1.2 1.1a4 4 0 0 0 2.2-1.1c.9-1 2.1-1.9 2.1-1.9s-.3 1.3.2 2.7q.7 1.8 2.2.4c1-.8 2-2.2 2.6-2.8l.7-1 1.1-1 1-5.4-.5-.7-1.4.7-1.7-.7-.5-.7-1 1.7-1 .6-.5.1-1.2.3-.5-1.1-.3-.8-.6-.3-2.4 2.5-1.4-.1-1.3 2.2-1 1.3-1 2.4z"/>
<path fill="#8f4620" d="M313.9 201.5s.2 1.3-.5 3.6-1.6 3.9-1.6 3.9 2.4-1.5 3.6-4.2c1.1-2.6.9-5.7.9-5.7zm3-2.5s.2.4 0 2.4l-.8 4 .6 1.2c.4.7 1.2 1.8 1.7 1.6s1.6-.9 2.2-2.2 1.1-2.7 1.4-2.9q.3-.1.4.2c0 .1.3 1.3.9 1.8.5.5 1.3 1.3 1.6 1 .4-.2.8-.9.7-1.2l-1.3-4-1.6-4-1-1.6-2.8.3z"/>
<path fill="#ab6d29" d="M321.7 195.3s.8 1.7.9 3.6c0 1.8-.4 3.8-.4 3.8s.2-2.3 0-3.7q-.6-2.1-.7-2.7z"/>
<path fill="#4d2a15" d="M325.6 202.6v2.3c-.2.8-1.3-.5-2-3s-.5-3-1-4.3l-.9-2.2 1.5-.3zm-7 4.6s2.8-1.5 3.1-6.2c.2-2.1-1.2-5.7-1.2-5.7s0 3.8-1 6.2l-1.9 4s.7-.2 1-.6l.8-1.1s.1.7-.1 1.6l-.6 1.8zm-2.3-8.1s-.1 1-1.3 2c-1 .9-3.1 1.9-3.1 1.9s1.5-1.4 2-2.6.1-2 .1-2-.2.6-.8 1.1-1.6.6-1.6.6 0-.6.7-1.3q0-.2.2-.7c.3-1 1.1-2.3 1.1-2.3s.7 1.4 1.3 2z"/>
<path fill="#1e2121" d="M324.7 193.6a18 18 0 0 0 .9 9s-1.4-2.2-1.7-3c-.4-1-.4-2.3-.7-3l-1-1c-.3-.2-1-.1-1-.1s.8-.4.8-1.1v-1l1.4.3h1.3zm-6 9.8s1.4-2 1.8-4.6 0-3.5 0-3.5-.6 0-1.1-.5-1.3-1.8-1.3-1.8.2 1.4 0 3c-.4 1.6-1.8 3-1.8 3s.8.4 1.4 0c1.5-.9 1.4-2.5 1.4-2.5s.3.2.3 2.2-.7 4.7-.7 4.7"/>
<path fill="#8f4620" d="M301.2 206.9c-.3 0 1-2 1.8-3.4l2.1-3.4s.3.3.8.3a4 4 0 0 0 2.4-1.7 7 7 0 0 0 1-2.5c0-.3-.1 1.8 1 3 1 1.1.9 1 1.3 1 0 0 0 1.4-.4 2.7l-.8 3.3s.2-2.5-.3-3.2-.6 0-1 1c-.3.9-1.1 1.5-1.1 1.5s.6-1.1 1-2.3.2-1.2 0-1.2q0-.2-.6.4-.8.7-.9.6c-.1-.1.5-.4.7-1l1-1.8q.4-.5.4-1t-.2-.7-1.4 1.2l-1.3 1.3-.9 2.6-1 2.8-.6.4.9-2.4 1-3.4q-.5-.5-.8 0l-2 2.8c-1 1.4-1 3.1-1.2 3.2l-1-.1z"/>
<path fill="#874f20" d="M325.7 192.1c0 .2 0 1.2-1 1.5a4 4 0 0 1-2.3-.2c-.2 0-.2-1.4-.2-1.4z"/>
<path fill="#b27129" d="M306 195.4s-.2.9-.8 2c-.5 1.2-1.4 2.2-1.3 2.4s.6.7 1.4.6 1.1-4.8 1.1-4.8l-.1-.2z"/>
<path fill="#dbad6c" d="M318.4 192.7s-.3 6.4-2.1 6.4-2.7-3.2-2.7-3.2-.1 4.3-2.4 4.3c-1 0-2.1-3.4-1.9-4 0 0-.4 2.3-1.5 3.2-2.2 1.7-3.5.9-3.6.6 0-.3 1.8-2 2-4.6 0 0 .4.1 1-.6s.7-1.6 1.3-1.5c.5.2 3-1 3-1l1.4-1.7s.2.5 1.2-.2a4 4 0 0 0 1.3-1.7l2.7 2.3z"/>
<path fill="#b27129" d="m311.4 186.6 2.7 3.8.6-.5q.7.8.4 1.2c-.4 1-1.3 2.3-.5 3.7 0 0-.3-.8.1-1.8s.7-1.4 1-1.3c.2 0 .6 5.8 1 5.8.3 0 .9-2.7.9-4.5s.3-.3.5 0c.2.4 1.8 3 3.1 2.6s1.2-2.8 1.2-3.3-1-3.7-1-3.7l-6.1-3.9-.5 1.6c-.3 1-3.4.3-3.4.3"/>
<path fill="#8f4620" d="M320 186s.4.4 1 1.9.7 6.4-.1 6.4-2.3-1-2.6-1.6 0-3.1-.4-3.8a8 8 0 0 1-1.2-2.7l.1-2 1.4-.5z"/>
<path fill="#4d2a15" d="m319.6 184.5 2.6 3.9 2.3 3.3 1.2.4a2.5 2.5 0 0 1-3.2.5c-.9-.5-.4-2-1.5-4.5s-3.2-3.6-3.2-3.6l.7-.7z"/>
<path fill="#1e2121" d="M318.2 182.4s2.3 1.5 3.1 3.2 1.3 2.7 2.3 4.3 2.1 2.1 2 2.2c0 .1-.7.3-1.2 0a5 5 0 0 1-1.4-1.6l-2.4-4.2c-.5-.7-2.3-2-2.3-1.9s.5.5 1.5 2.1a7 7 0 0 1 1.2 3.1l-1.1-2c-1-1.5-2.8-3.3-2.9-3s1 1.9 1.5 2.9l1.3 3.3-1.8-3c-1-1.5-1.2-1.3-1.6-1.8l-1.4-1.8s1 .3 1.8-.3c1-.6 1.4-1.5 1.4-1.5"/>
<path fill="#8f4620" d="M312.3 186.7s1.5.3 2.3.2h1.1s.5.6.5 1.4-.5 2.1-.8 2.2c-.2 0-.2-1.3-.5-1.8z"/>
<path fill="#b27129" d="M313.1 190.6s.5 1.6.1 3.9-1.7 4.6-2 4.6-.2-2.6-.4-4v-.4l-.5 1.6.4-2.6-.1-.8 1.6-2z"/>
<path fill="#8f4620" d="M310.3 192.3c-.5.5-1.2.4-1.8.6s-.9 1-.9 1 .5-.3.7-.2c.3.1.5.1.1 1.2s-2.2 3-2.1 3c0 0 2-1.5 2.5-2.5.7-.9.6-1.4.8-1.8 0-.3.8-.7 1-.7q.3-.2.6.7l.6 2.6.8-2.6c.2-1 .2-2.9.2-2.9l-.6-.1c-.5-.1-1.5-1-1.5-1s.2 2-.5 2.7z"/>
<path fill="#dbad6c" d="M318.3 180.8s.6 1.5-.7 2.6q-.5.5-1.4.6l-1.7-.6-4-1.6-2-.6-1-.3.2-1.1h3l3 1.2 1.3.5 1.1.2h.9l.7-.2.4-.3z"/>
<path fill="#ab6d29" d="M318.3 180.8c0 .3-.3 1.1-.6 1.3-.3 0-.8.3-2 0s-2.3-1-3.6-1.3q-1.9-.6-3.3-.7c-1.4-.1-.5-.4-.5-.4s2.3-.3 4.3.5c2 .7 2.9 1.2 4 1.2 1.4 0 1.7-.6 1.7-.6"/>
<path fill="#4d2a15" d="M307.6 179.6h.9q.6-.2.7-.3c.1-.1 0 .5-.2.7s-1 .1-1.2.1z"/>
<path fill="#dbad6c" d="M315.3 184.4s1.3 1.3 1 2.4l-2.7-1.2-4.2-1.7-2.3-.7-.3-.4.6-.8.2-.8 2.8.4 3 1z"/>
<path fill="#dbad6c" d="M312.4 186.3s2.4 3.1 1.8 4.1l-5.6-3.5-2 3.3-4.7-2.3s3.8-3 4.2-3.5l.5-.7 2 .4 3.4 1z"/>
<path fill="#ab6d29" d="M311 181.6s2 .5 3.3 1.2 3 .8 3 .8-.6.7-2 .8c-.4 0-1.4-.8-2-1.3s-2.6-1-2.6-1z"/>
<path fill="#4d2a15" d="M307.6 180.6s1.4.3 2.2.7c1 .3 1 .2 1.2.1q.4-.1.3-.2c-.1-.1.2 1-.5 1s-1-.3-1.8-.7c-1-.4-1.5 0-1.5 0v-1z"/>
<path fill="#ab6d29" d="M315.4 186s1 .7.9.9q-.3.1-1.6.4a3 3 0 0 1-2-.4c-.6-.2-1-1.3-2.5-2l-2.8-.9-1.3-.2 1-.4 3.2.6 2.8.9 1.7.8.6.4zm-5.4 2 .6 2.6-.4 1.5-.8.6-.8-.3-.3-.5s.4-.6 0-1.1c-.3-.6-2-1.2-2-1.2s.7-1.3 1.4-1.6c.8-.3.4-1.2.4-1.2z"/>
<path fill="#4d2a15" d="M306.8 182.8s.5.4 1.5.5q.8 0 2.2.5l1-.3-.5.4c.9.2 2 .7 2.5.9a6 6 0 0 1 2 1.3l-2-1-3-.9c-2-.3-4-.2-4.3-.4-.4 0 .1-.1.4-.3.2-.2.2-.7.2-.7"/>
<path fill="#ab6d29" d="M314 189.8s.7.7 0 .9-2.4.2-3.2-.7l-.6-1.2-2.4-1.7c-.7-.5.3-.5.3-.5l3.6 1.5z"/>
<path fill="#4d2a15" d="M314 189.9s-1.2-1.8-4.9-3.3l1.2-.2h-2l-.8-.3c-2-.6-2-.6-2.2-.8q-.2-.3-.6.3l-.7 1a5 5 0 0 1 2.5-.1c1.1.3 1 .7 1 1-.1.5-1.4 2-1.3 2.1.2 0 1-1.5 2-1.3q1.3.6 1.2.8c-.1.2 0-.6-.2-.8s-1.3-1.3-1-1.3q.6 0 3.3 1.2c1.8.9 2 1.1 2.6 1.7z"/>
<path fill="#dbad6c" d="M308.1 187s2.8.8 2.7 3.8-2.3 2.4-2.5 1.3.3.5 1 .4q1.2-.3 1-2.2 0-1.4-1-2.2l-1.2-1z"/>
<path fill="#6c3f18" d="M296 179.9s.4.2-.4 2.3-1.3 2.8-2.5 4.2c-2.2 2.4-3.2 2.5-3 3.5s1.1.8 1.3.8 4.3-6 4.3-6l.9-3.5-.2-1-.3-.3z"/>
<path fill="#dbad6c" d="M306.1 175.6s1.2 1.8.8 5c-.3 3.4-4.7 6.3-4.7 6.3l-6.4 4.4-2.7-.2h-.8q-.7-.3-.9-.5l-.5-1.2.9-1 2.3-2.3 1.4-2s.7-1.3.8-2.5q-.1-1.7-.2-1.7l.5.8.3 1.8-.2 2 1.6-1.2 2.2-1s.7 0 1-1.3c.5-1.2.9-3 .9-3.8l-.1-1.8h.3c.2 0 .7 1.2.7 1.7l.4 2.8 1-1 1-1.5q.3-.9.4-1.8"/>
<path fill="#904720" d="M306.8 192.2s.5.4.3 1.6-1.1 1.8-1.6 1.8V194l-1.4-1.2-1.5-.4.3-.3c.1-.2 1.7-.8 1.7-.8z"/>
<path fill="#ab6d29" d="M305.7 192.2s.5.4 1.2.3 1-1.3.6-1.8-.2 1-.7 1.3c-.4.1-1-1.2-1.5-1.3-.7-.1-1.6.3-1.9.7s1 0 1 0l.7.3h.5z"/>
<path fill="#904720" d="M296.5 187.6s.8.3 1.7.1l3.1-1 1.8-1.3c.7-.7 3.1-2.2 3.4-5.1.2-3-.4-4.7-.4-4.7s3.5 3.8.5 8.2c-2 2.8-3.8 3.7-3.8 3.7s3.4-1 3.7-.2-.1 2-.2 2.3c0 0 1.9.4 2.2 1.4.1.6-2.2-.7-4-.2s-2.5 1.4-2.5 1.4-.4-.5-2-.5c-1.5 0-2.2.7-3.1.7-1 0-3.7-.6-4.2-1.4l1.8-2.2c.8-1.6 2-1.2 2-1.2"/>
<path fill="#ab6d29" d="M303.8 179.7s1.3-1.3 1.8-2.3q.5-1.6.5-1.8c0-.2.2 1-.6 2.3s-1.7 2-1.7 2z"/>
<path fill="#904720" d="M303.4 176.7s1.1 4.4 0 6.1c-1.3 1.8-6.5 4.1-6.5 4.1s4-2.1 5.2-4.6 1.1-4.5 1.1-4.5z"/>
<path fill="#1e2121" d="M298.2 187.7s1.7 0 2.6-.6 2.3-1.7 2.3-1.7-1.2 1-1.6 1q-.6-.1-.5-.9s-.3.8-1 1.4c-.8.5-1.8.8-1.8.8m3.9-5.4s1.4-1.4 1.4-4.4-1.2-2.5-1.2-2.5 1 .7.7 3c-.2 2.3-1 4-1 4zm1.7.3s1.6-.3 2.1-1.3.6-2.7.6-2.7-.2 1.7-.7 2.3-2 1.7-2 1.7m-1 9.5s.8-1.5 1.9-1.1 1 1.2 1 1.2-.4-.6-1.2-.7-1.6.6-1.6.6zm-2.9-2.7 3.7-1 3-1s-1.2 1.3-3 2.3c-1.6 1-3 1-3 1s3.2-1.4 3.4-2c0 0-3 .9-4 .7z"/>
<path fill="#fff" d="M296.2 185.8a2.6 2.7 67.8 0 1-3 4.4"/>
<path fill="#f16e16" d="M296.1 186.1a2.3 2.4 67.8 0 1-2.7 3.8"/>
<path d="M295 187.4a1 1 0 1 1-.8 1.4"/>
<path fill="#d5d3ca" d="M295.2 188.6a.3.3 0 1 1-.6-.1l.3-.3a.3.3 0 0 1 .3.4"/>
<path fill="#ab6d29" d="M296.8 184.2s1.6-1.5 1.9-3.5q.3-3.1.1-3.2l.9 1 .3 1.4-.5 2.6 1.6-.8c.6-.8 1.5-4.2 1.2-6.3 0 0 .4 1-.2 3.8-.6 2.9-1 3.2-3.2 4.4a10 10 0 0 0-3.8 4.2l-2.3 3 1.6-2.6c.8-1.8 1.7-2.8 1.7-2.8z"/>
<path fill="#904720" d="M296.1 185.4s.7-2 .7-3.1-.7-2.4-.7-2.4 1-.1 1 2.2c.1 2.4-.4 2.6-1 3.3"/>
<path fill="#4d2a15" d="M299.2 182.8s.7-1.7.7-3c0-1.1-1-2.3-1-2.3s1.1.3 1.2 2.4c.1 2.2-.2 2.2-1 3z"/>
<path fill="#ab6d29" d="M292.1 191s-.6-.8-.5-1.3 0-.7 1.4-2.2a13 13 0 0 0 2.5-3c.3-.9 1.5-3 .6-4.6 0 0 .6 2.4-.8 4.3a16 16 0 0 1-3.3 3.7q-1.5 1-1.2 1.7 0 .9.3 1c.4.3 1 .3 1 .3z"/>
<path fill="#6c4119" d="M305.5 195.6s.3 0 .5-.3.2-1-.3-1.6a3 3 0 0 0-1.7-1.1l-2-.4s.4.8 1.3 1.3c.4.2 1.2 0 1.6.4s.6 1.7.6 1.7"/>
<path fill="#6c4119" d="M305.5 195.6s1.5-.5 1.6-2l-.1-1.2s.2.5.8.7l.6-.2s-1 2.8-3 2.7z"/>
<path fill="#bf802d" d="m294.8 195.3.8.8.9 1 3.7-1 1.2-1.2-1-1.6h-2.7l-3 2z"/>
<path fill="#f9c83a" stroke="#8f4620" stroke-width=".4" d="m303.8 194-.7-.9-.7-.6c-.6-.3-2.8-.3-2.8-.3l-1.6.3s-.5.5-1.3.3l-2.6-1-1.9-.7s-1.3-.6-1.6-.5-1.5 1.3-1.5 1.3-.2.7.3.7-.7.2-.7.2-2.8 1.6-3.2 4.4c-.5 2.7 4.6 6.8 6.5 4.7 0 0-2.8-2-2.2-3.7q.7-2.6 4-3c2.1 0 2.3-.3 3.2-1s2.7-1 4 .3-5 2.7-5 2.7l.7 1s7.7-2.7 7-4.1z"/>
<path fill="#fcf3d8" d="M289.4 200.7s-3.1-.8-2.9-3.7c.3-2.8 3-3.9 3.4-4s.3-1.6.8-1.8a2 2 0 0 1 2 .5l1.4.8s-5.5 2.4-5.5 5.6c0 2.1.8 2.5.8 2.5z"/>
<path fill="#fdeaaf" d="M303.8 193.8s-.4.2-.5 0c-.6-1-2-1.4-3-1.3-1.5 0-2.4.5-3.4.5s-.7-.1-1.8-.2c-1.1 0-3.5-1.8-4-1.6q-.9.5-.8 1.1c.1.4-1 .4-1 .1-.2-.2.7-1.7 1.7-1.7 2.6 0 4.6 1.7 5.8 1.7s1.7-.7 3.4-.7 3.5.8 3.6 2z"/>
<path fill="#513625" d="M295.2 195.8s1.3-.2 2.2-.6l2.2-.8-2 1.2-2 .5z"/>
<path fill="#f9c83a" d="M290.8 202.3c-.2 0-1.3 0-3-1.1-1.8-1.2-2.3-3.2-2.3-3.2s-.4-1.9 1.4-3.8 2.1-1.3 2.2-1.2q0 0 0 .1l-1.7 1.3-.7 1.3-.7 1.2v1.4l.8 1.5 2.5 1.5z"/>
<path fill="#8b5122" d="M289.8 191.4v-.2.3-.3.3-.3l-.3.2h.2v-.2l-.2.2.2-.2h-.2v.2l.2-.2h-.2.2-.2.2-.2.2l-.2-.1.2.1-.2-.1-.1.1-.4.5-.2.6v.1q0 .5.4.6h1.4l.1-.4-.3-.2-.7.1h-.3v-.4l.3-.5.2-.1zl.1-.2-.3-.2-.2.3.3.2z"/>
<path fill="#f9c83a" d="m295.1 194.1-3.8 1.1c-.3.2 1.2 0 2.4 0l1.3.1.9-.2c1.4-.5 4.4-1.6 4.7-.3.2 1-4.1 2-4.1 2v.5l3.4-.9 1.9-1 .5-.9-1.7-1.2H298l-1.2.3z"/>
<path fill="#8f4620" d="M289.7 199.5c.3.8.9 2 1.6 2.3 0 .1-.5.5-1.7.1-1.2-.3-3-.7-4.1-4v.7l.7 1.3 1.1 1.2 1.9 1.1 1.2.3 1-.2.6-.4-1.1-1-1.4-2.3z"/>
<path fill="#fcca3d" d="M302.1 232.6v1.2h-.7v-1.5zm-4.7-34.4-1.2-1.4-.8.1 1.2 1.8z"/>
<path fill="#816c2a" d="m302 235.5-1.7-1-2.4-.6v.3h.2l2.1.6 1.7 1zm0-3.1h-3l-1.3.7.1.2a4 4 0 0 1 2.6-.7l1.4.1v-.3zm-.4-2.7-2.6 1.4-1.6 1.2.2.2.5-.4 2.6-1.6 1-.5zm-1.7-2.7s0 .7-.3 1.2l-.6.8-2.5 2.6.2.3 2.5-2.7.7-.8q.4-1 .3-1.4zm-2.1-1.2a4 4 0 0 1-.2 2.2l-1 2.2-.4.9-.2.3.2.2s1.3-2.2 1.7-3.5q.3-.7.2-1.5v-.9zm-2.1-.3v1.2q0 1-.2 1.7l-.7 1.8-.3 1 .3.1 1-2.8q.3-.8.2-1.8v-1.2zm-1.6 0v1.2l-.3 2-.5 2.5h.3l.2-.8.3-1.7c.3-1 .3-3.2.3-3.2zm-1.8.3v2.9l-.4 2.5h.3l.4-2.4.1-1.4v-1.6zm-1.3.2v.2l-.3 2.2-.6 1.9-.2 1h.3l.8-2.9.3-2.2v-.2zm-1.6 0v.7q.1 1-.4 2l-1.3 1.6-.7.4-.2.2.1.3s1.7-1 2.4-2.3q.5-1.2.5-2.2v-.7zm-.9-.3v.2l-1 1.9q-.6.6-1.3 1.1l-.8.5.2.3s1.4-.8 2.1-1.7l1.1-2.2h-.3zm-1.5-.7v.1l-1 1.3q-.2.3-.9.6l-.6.4.1.3.7-.4 1-.7.7-.9.3-.6zm-.7-1-.3.3-.9 1-.5.4-.4.3.1.3.7-.4c.7-.6 1.5-1.7 1.5-1.7zm-.4-.6-.2.2-.9.6-.8.3v.4l.6-.3c.7-.3 1.5-1 1.5-1zm-.2-.7-.8.4-.6.3-.4.2v.3q.3 0 .5-.2l1.4-.7zm-.4-1.3-.5.5-.9.3v.3a2 2 0 0 0 1.2-.5l.5-.4zm.1-2.2-.6.5q-.5.4-.7 1h-.1v.5l.2-.2.2-.2q.1-.4.7-.9l.5-.5zm10.7-12.8h1.6l.1-.2v-.1h-.1v.3-.1h-.2.1-.1v-.2l.1.1v-.1.1-.1h-1.5zm0-1h.2q.3 0 .6-.2.6-.2 1-.6l.5-.3h.3v-.4l-.6.2-1 .7-.8.3zm0-1.5.7-.2 1.2-.8q.6-.4 1-.4v-.3q-.4 0-.8.2l-1.2.7-1 .5v.3zm-.6-1.4 1.5-1 1.6-1.2-.1-.3-1.7 1.2q-.8.8-1.5 1l.2.4zm-1-1.5 3.2-2.3-.2-.3-3.2 2.3zm-1.5-1.6a21 21 0 0 1 3.4-2.3l-.2-.2-1.5 1-1.9 1.3zm-1-1 2.6-1.8.8-.5-.1-.3-.8.5-2.7 2z"/>
<path fill="#78732e" d="m294.5 195.4.1.1 2.5 2.5q1.6 1.9 1.8 4.1v.5a10 10 0 0 1-2.5 5c-2 2.3-4.8 4.4-7 6.4a18 18 0 0 0-3 3.1 6 6 0 0 0-1.2 3.7 6 6 0 0 0 1.1 3.4 4 4 0 0 0 3.9 2c1.8 0 3.8-.7 5.6-.7q2.4-.2 4.4 1.9c1.3 1.4 1.7 3.6 1.7 5.7q0 2.4-.4 4.2l-.4 1.8h.3s.8-2.8.8-6c0-2.1-.4-4.5-1.8-6a6 6 0 0 0-4.6-1.9c-1.9 0-3.9.6-5.6.6a4 4 0 0 1-3.6-1.8 6 6 0 0 1-1-3.3v-.1q0-2.4 1.9-4.3c1.8-2 4.5-4 7-6.2 2.3-2.2 4.4-4.5 4.7-7.5v-.5c0-1.8-1.1-3.5-2.3-4.8l-2.3-2.2z"/>
<path fill="#a8ac71" d="m292.1 198.4-.4-.3-.3.1.6.5zm1 1-.3-.3h-.1l.2.4z"/>
<path fill="#78732e" d="m294.5 201-.2-.4-.2.2zm1 1.5-.1-.3h-.3l.4.5zm.7 1.5-.2-.4v.4zm0 1.5v-.4l-.2-.1v.5zm0 1v-.5l-.1-.1v.6zM284 221.1l.1-.7c.1-.3-.3.1-.3.1v.5z"/>
<path fill="#fff" d="m284 222.1-.3.1.1.5.1-.2z"/>
<path fill="#78732e" d="M284 223.9v-.4h-.2v.6z"/>
<path fill="#fff" d="M284 224.9v-.4h-.1v.4zm.3 1.4v-.4l-.2.1zm.4 1.4-.1-.5h-.2l.2.6z"/>
<path fill="#a8ac71" d="m285.6 229.7-.2-.3h-.2zm1.5 1.4-.5-.3c-.3-.3-.1.2-.1.2l.4.2zm9.8.8-.4-.3q-.2-.1.2.4c.1.3.2-.1.2-.1"/>
<path fill="#fff" d="m297.7 232.5-.3-.3v.3h.2z"/>
<path fill="#a8ac71" d="M297.9 233q-.2-.1-.2.1l.2.3zm0 1.3v-.5q0-.1-.1 0v.5z"/>
<path fill="#fff" d="m297.4 235.7.2-.4h-.2zm-14.8-52s.5-.2.1.4l.3-.5h-.5z"/>
<path fill="#f9c83a" d="m294.6 195 .4.3h-.5z"/>
<path fill="#8f4620" d="m295 195.3-.7.1c-.2 0 .2-.2.2-.2l.4.1z"/>
<path fill="#977c2e" d="M301 239.1s.3 0 .5-.3-.1.4-.1.4l-.3.1-.2-.2z"/>
</svg>

After

Width:  |  Height:  |  Size: 83 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-my" viewBox="0 0 640 480">
<g clip-path="url(#my-a)">
<path fill="#C00" d="M0 0h640v480H0z"/>
<path fill="#C00" d="M0 0h640v34.3H0z"/>
<path fill="#fff" d="M0 34.3h640v34.3H0z"/>
<path fill="#C00" d="M0 68.6h640v34.3H0z"/>
<path fill="#fff" d="M0 102.9h640V137H0z"/>
<path fill="#C00" d="M0 137.1h640v34.3H0z"/>
<path fill="#fff" d="M0 171.4h640v34.3H0z"/>
<path fill="#C00" d="M0 205.7h640V240H0z"/>
<path fill="#fff" d="M0 240h640v34.3H0z"/>
<path fill="#C00" d="M0 274.3h640v34.3H0z"/>
<path fill="#fff" d="M0 308.6h640v34.3H0z"/>
<path fill="#C00" d="M0 342.9h640V377H0z"/>
<path fill="#fff" d="M0 377.1h640v34.3H0z"/>
<path fill="#C00" d="M0 411.4h640v34.3H0z"/>
<path fill="#fff" d="M0 445.7h640V480H0z"/>
<path fill="#006" d="M0 .5h320v274.3H0z"/>
<path fill="#FC0" d="m207.5 73.8 6 40.7 23-34-12.4 39.2 35.5-20.8-28.1 30 41-3.2-38.3 14.8 38.3 14.8-41-3.2 28.1 30-35.5-20.8 12.3 39.3-23-34.1-6 40.7-5.9-40.7-23 34 12.4-39.2-35.5 20.8 28-30-41 3.2 38.4-14.8-38.3-14.8 41 3.2-28.1-30 35.5 20.8-12.4-39.3 23 34.1zm-33.3 1.7a71 71 0 0 0-100 65 71 71 0 0 0 100 65 80 80 0 0 1-83.2 6.2 80 80 0 0 1-43.4-71.2 80 80 0 0 1 126.6-65"/>
</g>
<defs>
<clipPath id="my-a">
<path fill="#fff" d="M0 0h640v480H0z"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-no" viewBox="0 0 640 480">
<path fill="#ed2939" d="M0 0h640v480H0z"/>
<path fill="#fff" d="M180 0h120v480H180z"/>
<path fill="#fff" d="M0 180h640v120H0z"/>
<path fill="#002664" d="M210 0h60v480h-60z"/>
<path fill="#002664" d="M0 210h640v60H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 318 B

+36
View File
@@ -0,0 +1,36 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-nz" viewBox="0 0 640 480">
<defs>
<g id="nz-b">
<g id="nz-a">
<path d="M0-.3v.5l1-.5z"/>
<path d="M.2.3 0-.1l1-.2z"/>
</g>
<use xlink:href="#nz-a" transform="scale(-1 1)"/>
<use xlink:href="#nz-a" transform="rotate(72 0 0)"/>
<use xlink:href="#nz-a" transform="rotate(-72 0 0)"/>
<use xlink:href="#nz-a" transform="scale(-1 1)rotate(72)"/>
</g>
</defs>
<path fill="#00247d" fill-rule="evenodd" d="M0 0h640v480H0z"/>
<g transform="translate(-111 36.1)scale(.66825)">
<use xlink:href="#nz-b" width="100%" height="100%" fill="#fff" transform="translate(900 120)scale(45.4)"/>
<use xlink:href="#nz-b" width="100%" height="100%" fill="#cc142b" transform="matrix(30 0 0 30 900 120)"/>
</g>
<g transform="rotate(82 525.2 114.6)scale(.66825)">
<use xlink:href="#nz-b" width="100%" height="100%" fill="#fff" transform="rotate(-82 519 -457.7)scale(40.4)"/>
<use xlink:href="#nz-b" width="100%" height="100%" fill="#cc142b" transform="rotate(-82 519 -457.7)scale(25)"/>
</g>
<g transform="rotate(82 525.2 114.6)scale(.66825)">
<use xlink:href="#nz-b" width="100%" height="100%" fill="#fff" transform="rotate(-82 668.6 -327.7)scale(45.4)"/>
<use xlink:href="#nz-b" width="100%" height="100%" fill="#cc142b" transform="rotate(-82 668.6 -327.7)scale(30)"/>
</g>
<g transform="translate(-111 36.1)scale(.66825)">
<use xlink:href="#nz-b" width="100%" height="100%" fill="#fff" transform="translate(900 480)scale(50.4)"/>
<use xlink:href="#nz-b" width="100%" height="100%" fill="#cc142b" transform="matrix(35 0 0 35 900 480)"/>
</g>
<path fill="#012169" d="M0 0h320v240H0z"/>
<path fill="#fff" d="m37.5 0 122 90.5L281 0h39v31l-120 89.5 120 89V240h-40l-120-89.5L40.5 240H0v-30l119.5-89L0 32V0z"/>
<path fill="#c8102e" d="M212 140.5 320 220v20l-135.5-99.5zm-92 10 3 17.5-96 72H0zM320 0v1.5l-124.5 94 1-22L295 0zM0 0l119.5 88h-30L0 21z"/>
<path fill="#fff" d="M120.5 0v240h80V0zM0 80v80h320V80z"/>
<path fill="#c8102e" d="M0 96.5v48h320v-48zM136.5 0v240h48V0z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

+6
View File
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ph" viewBox="0 0 640 480">
<path fill="#0038a8" d="M0 0h640v240H0z"/>
<path fill="#ce1126" d="M0 240h640v240H0z"/>
<path fill="#fff" d="M415.7 240 0 480V0"/>
<path fill="#fcd116" d="M26.7 42.4 41 55l16.6-9.2-7.4 17.5 14 13-19-1.6-8.1 17.2-4.3-18.5L14 71l16.3-10zm323.8 172.3.4 19 18 6.3-18 6.2-.4 19-11.5-15.1-18.2 5.5 10.8-15.6-10.8-15.6 18.2 5.5zM37.2 388.1l8 17.2 19-1.6-13.9 13 7.4 17.5-16.6-9.1-14.4 12.4 3.6-18.7L14 409l18.9-2.4zm114.2-249-6.2 6.2 3.1 47-3 .3-5.7-42.9-5.1 5 7.6 38.4a48 48 0 0 0-17.2 7.1l-21.7-32.4H96l26.4 34.3-2.4 2-31.1-35.5h-8.8v8.8l35.4 31-2 2.5-34.3-26.3v7.1l32.5 21.7q-5.2 7.8-7.1 17.2L66.3 223l-5.1 5 42.9 5.7q-.3 1.6-.3 3.1l-47-3-6.2 6.2 6.2 6.2 47-3.1.3 3.1-42.9 5.7 5 5 38.4-7.6a48 48 0 0 0 7.1 17.2l-32.5 21.7v7.2l34.3-26.3 2 2.4-35.4 31v8.8H89l31-35.4 2.5 2L96 312.2h7.2l21.7-32.5q7.8 5.2 17.2 7.1l-7.6 38.4 5 5 5.7-42.9q1.5.3 3.1.3l-3 47 6.1 6.2 6.3-6.2-3.1-47 3-.3 5.7 43 5.1-5.1-7.6-38.4a48 48 0 0 0 17.2-7.1l21.7 32.5h7.2l-26.4-34.3 2.4-2 31.1 35.4h8.8v-8.8l-35.4-31 2-2.4 34.3 26.3v-7.2l-32.5-21.7q5.2-7.8 7.1-17.2l38.3 7.6 5.1-5-42.9-5.7q.3-1.5.3-3.1l47 3 6.2-6.1-6.2-6.2-47 3-.3-3 42.9-5.7-5-5-38.4 7.5a48 48 0 0 0-7.1-17.2l32.5-21.7v-7.1l-34.3 26.3-2-2.4 35.4-31v-8.9H214l-31 35.5-2.5-2 26.4-34.3h-7.2L178 200.2q-7.8-5.2-17.2-7.1l7.6-38.3-5-5-5.7 42.8-3.1-.3 3-47z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+6
View File
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-pl" viewBox="0 0 640 480">
<g fill-rule="evenodd">
<path fill="#fff" d="M640 480H0V0h640z"/>
<path fill="#dc143c" d="M640 480H0V240h640z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 219 B

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ro" viewBox="0 0 640 480">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="#00319c" d="M0 0h213.3v480H0z"/>
<path fill="#ffde00" d="M213.3 0h213.4v480H213.3z"/>
<path fill="#de2110" d="M426.7 0H640v480H426.7z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 302 B

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ru" viewBox="0 0 640 480">
<path fill="#fff" d="M0 0h640v160H0z"/>
<path fill="#0039a6" d="M0 160h640v160H0z"/>
<path fill="#d52b1e" d="M0 320h640v160H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 225 B

+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-se" viewBox="0 0 640 480">
<path fill="#005293" d="M0 0h640v480H0z"/>
<path fill="#fecb00" d="M176 0v192H0v96h176v192h96V288h368v-96H272V0z"/>
</svg>

After

Width:  |  Height:  |  Size: 209 B

+13
View File
@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-sg" viewBox="0 0 640 480">
<defs>
<clipPath id="sg-a">
<path fill-opacity=".7" d="M0 0h640v480H0z"/>
</clipPath>
</defs>
<g fill-rule="evenodd" clip-path="url(#sg-a)">
<path fill="#fff" d="M-20 0h720v480H-20z"/>
<path fill="#df0000" d="M-20 0h720v240H-20z"/>
<path fill="#fff" d="M146 40.2a84.4 84.4 0 0 0 .8 165.2 86 86 0 0 1-106.6-59 86 86 0 0 1 59-106c16-4.6 30.8-4.7 46.9-.2z"/>
<path fill="#fff" d="m133 110 4.9 15-13-9.2-12.8 9.4 4.7-15.2-12.8-9.3 15.9-.2 5-15 5 15h15.8zm17.5 52 5 15.1-13-9.2-12.9 9.3 4.8-15.1-12.8-9.4 15.9-.1 4.9-15.1 5 15h16zm58.5-.4 4.9 15.2-13-9.3-12.8 9.3 4.7-15.1-12.8-9.3 15.9-.2 5-15 5 15h15.8zm17.4-51.6 4.9 15.1-13-9.2-12.8 9.3 4.8-15.1-12.9-9.4 16-.1 4.8-15.1 5 15h16zm-46.3-34.3 5 15.2-13-9.3-12.9 9.4 4.8-15.2-12.8-9.4 15.8-.1 5-15.1 5 15h16z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 889 B

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-th" viewBox="0 0 640 480">
<g fill-rule="evenodd">
<path fill="#f4f5f8" d="M0 0h640v480H0z"/>
<path fill="#2d2a4a" d="M0 162.5h640v160H0z"/>
<path fill="#a51931" d="M0 0h640v82.5H0zm0 400h640v80H0z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 284 B

+8
View File
@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-tr" viewBox="0 0 640 480">
<g fill-rule="evenodd">
<path fill="#e30a17" d="M0 0h640v480H0z"/>
<path fill="#fff" d="M407 247.5c0 66.2-54.6 119.9-122 119.9s-122-53.7-122-120 54.6-119.8 122-119.8 122 53.7 122 119.9"/>
<path fill="#e30a17" d="M413 247.5c0 53-43.6 95.9-97.5 95.9s-97.6-43-97.6-96 43.7-95.8 97.6-95.8 97.6 42.9 97.6 95.9z"/>
<path fill="#fff" d="m430.7 191.5-1 44.3-41.3 11.2 40.8 14.5-1 40.7 26.5-31.8 40.2 14-23.2-34.1 28.3-33.9-43.5 12-25.8-37z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 549 B

+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-us" viewBox="0 0 640 480">
<path fill="#bd3d44" d="M0 0h640v480H0"/>
<path stroke="#fff" stroke-width="37" d="M0 55.3h640M0 129h640M0 203h640M0 277h640M0 351h640M0 425h640"/>
<path fill="#192f5d" d="M0 0h364.8v258.5H0"/>
<marker id="us-a" markerHeight="30" markerWidth="30">
<path fill="#fff" d="m14 0 9 27L0 10h28L5 27z"/>
</marker>
<path fill="none" marker-mid="url(#us-a)" d="m0 0 16 11h61 61 61 61 60L47 37h61 61 60 61L16 63h61 61 61 61 60L47 89h61 61 60 61L16 115h61 61 61 61 60L47 141h61 61 60 61L16 166h61 61 61 61 60L47 192h61 61 60 61L16 218h61 61 61 61 60z"/>
</svg>

After

Width:  |  Height:  |  Size: 648 B

+17
View File
@@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-za" viewBox="0 0 640 480">
<defs>
<clipPath id="za-a">
<path fill-opacity=".7" d="M-71.9 0h682.7v512H-71.9z"/>
</clipPath>
</defs>
<g clip-path="url(#za-a)" transform="translate(67.4)scale(.93748)">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="#000001" d="M-71.9 407.8V104.4L154 256.1z"/>
<path fill="#000c8a" d="m82.2 512.1 253.6-170.6H696V512H82.2z"/>
<path fill="#e1392d" d="M66 0h630v170.8H335.7S69.3-1.7 66 0"/>
<path fill="#ffb915" d="M-71.9 64v40.4L154 256-72 407.8v40.3l284.5-192z"/>
<path fill="#007847" d="M-71.9 64V0h95l301.2 204h371.8v104.2H324.3L23 512h-94.9v-63.9l284.4-192L-71.8 64z"/>
<path fill="#fff" d="M23 0h59.2l253.6 170.7H696V204H324.3zm0 512.1h59.2l253.6-170.6H696v-33.2H324.3L23 512z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 860 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M312 32c-13.3 0-24 10.7-24 24s10.7 24 24 24l25.7 0 34.6 64-149.4 0-27.4-38C191 99.7 183.7 96 176 96l-56 0c-13.3 0-24 10.7-24 24s10.7 24 24 24l43.7 0 22.1 30.7-26.6 53.1c-10-2.5-20.5-3.8-31.2-3.8C57.3 224 0 281.3 0 352s57.3 128 128 128c65.3 0 119.1-48.9 127-112l49 0c8.5 0 16.3-4.5 20.7-11.8l84.8-143.5 21.7 40.1C402.4 276.3 384 312 384 352c0 70.7 57.3 128 128 128s128-57.3 128-128s-57.3-128-128-128c-13.5 0-26.5 2.1-38.7 6L375.4 48.8C369.8 38.4 359 32 347.2 32L312 32zM458.6 303.7l32.3 59.7c6.3 11.7 20.9 16 32.5 9.7s16-20.9 9.7-32.5l-32.3-59.7c3.6-.6 7.4-.9 11.2-.9c39.8 0 72 32.2 72 72s-32.2 72-72 72s-72-32.2-72-72c0-18.6 7-35.5 18.6-48.3zM133.2 368l65 0c-7.3 32.1-36 56-70.2 56c-39.8 0-72-32.2-72-72s32.2-72 72-72c1.7 0 3.4 .1 5.1 .2l-24.2 48.5c-9 18.1 4.1 39.4 24.3 39.4zm33.7-48l50.7-101.3 72.9 101.2-.1 .1-123.5 0zm90.6-128l108.5 0L317 274.8 257.4 192z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M96 0C43 0 0 43 0 96L0 416c0 53 43 96 96 96l288 0 32 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l0-64c17.7 0 32-14.3 32-32l0-320c0-17.7-14.3-32-32-32L384 0 96 0zm0 384l256 0 0 64L96 448c-17.7 0-32-14.3-32-32s14.3-32 32-32zm32-240c0-8.8 7.2-16 16-16l192 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-192 0c-8.8 0-16-7.2-16-16zm16 48l192 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-192 0c-8.8 0-16-7.2-16-16s7.2-16 16-16z"/></svg>

After

Width:  |  Height:  |  Size: 626 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M149.1 64.8L138.7 96 64 96C28.7 96 0 124.7 0 160L0 416c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-256c0-35.3-28.7-64-64-64l-74.7 0L362.9 64.8C356.4 45.2 338.1 32 317.4 32L194.6 32c-20.7 0-39 13.2-45.5 32.8zM256 192a96 96 0 1 1 0 192 96 96 0 1 1 0-192z"/></svg>

After

Width:  |  Height:  |  Size: 489 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M135.2 117.4L109.1 192l293.8 0-26.1-74.6C372.3 104.6 360.2 96 346.6 96L165.4 96c-13.6 0-25.7 8.6-30.2 21.4zM39.6 196.8L74.8 96.3C88.3 57.8 124.6 32 165.4 32l181.2 0c40.8 0 77.1 25.8 90.6 64.3l35.2 100.5c23.2 9.6 39.6 32.5 39.6 59.2l0 144 0 48c0 17.7-14.3 32-32 32l-32 0c-17.7 0-32-14.3-32-32l0-48L96 400l0 48c0 17.7-14.3 32-32 32l-32 0c-17.7 0-32-14.3-32-32l0-48L0 256c0-26.7 16.4-49.6 39.6-59.2zM128 288a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zm288 32a32 32 0 1 0 0-64 32 32 0 1 0 0 64z"/></svg>

After

Width:  |  Height:  |  Size: 713 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M512 80c0 18-14.3 34.6-38.4 48c-29.1 16.1-72.5 27.5-122.3 30.9c-3.7-1.8-7.4-3.5-11.3-5C300.6 137.4 248.2 128 192 128c-8.3 0-16.4 .2-24.5 .6l-1.1-.6C142.3 114.6 128 98 128 80c0-44.2 86-80 192-80S512 35.8 512 80zM160.7 161.1c10.2-.7 20.7-1.1 31.3-1.1c62.2 0 117.4 12.3 152.5 31.4C369.3 204.9 384 221.7 384 240c0 4-.7 7.9-2.1 11.7c-4.6 13.2-17 25.3-35 35.5c0 0 0 0 0 0c-.1 .1-.3 .1-.4 .2c0 0 0 0 0 0s0 0 0 0c-.3 .2-.6 .3-.9 .5c-35 19.4-90.8 32-153.6 32c-59.6 0-112.9-11.3-148.2-29.1c-1.9-.9-3.7-1.9-5.5-2.9C14.3 274.6 0 258 0 240c0-34.8 53.4-64.5 128-75.4c10.5-1.5 21.4-2.7 32.7-3.5zM416 240c0-21.9-10.6-39.9-24.1-53.4c28.3-4.4 54.2-11.4 76.2-20.5c16.3-6.8 31.5-15.2 43.9-25.5l0 35.4c0 19.3-16.5 37.1-43.8 50.9c-14.6 7.4-32.4 13.7-52.4 18.5c.1-1.8 .2-3.5 .2-5.3zm-32 96c0 18-14.3 34.6-38.4 48c-1.8 1-3.6 1.9-5.5 2.9C304.9 404.7 251.6 416 192 416c-62.8 0-118.6-12.6-153.6-32C14.3 370.6 0 354 0 336l0-35.4c12.5 10.3 27.6 18.7 43.9 25.5C83.4 342.6 135.8 352 192 352s108.6-9.4 148.1-25.9c7.8-3.2 15.3-6.9 22.4-10.9c6.1-3.4 11.8-7.2 17.2-11.2c1.5-1.1 2.9-2.3 4.3-3.4l0 3.4 0 5.7 0 26.3zm32 0l0-32 0-25.9c19-4.2 36.5-9.5 52.1-16c16.3-6.8 31.5-15.2 43.9-25.5l0 35.4c0 10.5-5 21-14.9 30.9c-16.3 16.3-45 29.7-81.3 38.4c.1-1.7 .2-3.5 .2-5.3zM192 448c56.2 0 108.6-9.4 148.1-25.9c16.3-6.8 31.5-15.2 43.9-25.5l0 35.4c0 44.2-86 80-192 80S0 476.2 0 432l0-35.4c12.5 10.3 27.6 18.7 43.9 25.5C83.4 438.6 135.8 448 192 448z"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M192 64C86 64 0 150 0 256S86 448 192 448l256 0c106 0 192-86 192-192s-86-192-192-192L192 64zM496 168a40 40 0 1 1 0 80 40 40 0 1 1 0-80zM392 304a40 40 0 1 1 80 0 40 40 0 1 1 -80 0zM168 200c0-13.3 10.7-24 24-24s24 10.7 24 24l0 32 32 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-32 0 0 32c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-32-32 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l32 0 0-32z"/></svg>

After

Width:  |  Height:  |  Size: 602 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M64 0C28.7 0 0 28.7 0 64L0 352c0 35.3 28.7 64 64 64l176 0-10.7 32L160 448c-17.7 0-32 14.3-32 32s14.3 32 32 32l256 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-69.3 0L336 416l176 0c35.3 0 64-28.7 64-64l0-288c0-35.3-28.7-64-64-64L64 0zM512 64l0 224L64 288 64 64l448 0z"/></svg>

After

Width:  |  Height:  |  Size: 491 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M176 24c0-13.3-10.7-24-24-24s-24 10.7-24 24l0 40c-35.3 0-64 28.7-64 64l-40 0c-13.3 0-24 10.7-24 24s10.7 24 24 24l40 0 0 56-40 0c-13.3 0-24 10.7-24 24s10.7 24 24 24l40 0 0 56-40 0c-13.3 0-24 10.7-24 24s10.7 24 24 24l40 0c0 35.3 28.7 64 64 64l0 40c0 13.3 10.7 24 24 24s24-10.7 24-24l0-40 56 0 0 40c0 13.3 10.7 24 24 24s24-10.7 24-24l0-40 56 0 0 40c0 13.3 10.7 24 24 24s24-10.7 24-24l0-40c35.3 0 64-28.7 64-64l40 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-40 0 0-56 40 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-40 0 0-56 40 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-40 0c0-35.3-28.7-64-64-64l0-40c0-13.3-10.7-24-24-24s-24 10.7-24 24l0 40-56 0 0-40c0-13.3-10.7-24-24-24s-24 10.7-24 24l0 40-56 0 0-40zM160 128l192 0c17.7 0 32 14.3 32 32l0 192c0 17.7-14.3 32-32 32l-192 0c-17.7 0-32-14.3-32-32l0-192c0-17.7 14.3-32 32-32zm192 32l-192 0 0 192 192 0 0-192z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M349.4 44.6c5.9-13.7 1.5-29.7-10.6-38.5s-28.6-8-39.9 1.8l-256 224c-10 8.8-13.6 22.9-8.9 35.3S50.7 288 64 288l111.5 0L98.6 467.4c-5.9 13.7-1.5 29.7 10.6 38.5s28.6 8 39.9-1.8l256-224c10-8.8 13.6-22.9 8.9-35.3s-16.6-20.7-30-20.7l-111.5 0L349.4 44.6z"/></svg>

After

Width:  |  Height:  |  Size: 477 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M575.8 255.5c0 18-15 32.1-32 32.1l-32 0 .7 160.2c0 2.7-.2 5.4-.5 8.1l0 16.2c0 22.1-17.9 40-40 40l-16 0c-1.1 0-2.2 0-3.3-.1c-1.4 .1-2.8 .1-4.2 .1L416 512l-24 0c-22.1 0-40-17.9-40-40l0-24 0-64c0-17.7-14.3-32-32-32l-64 0c-17.7 0-32 14.3-32 32l0 64 0 24c0 22.1-17.9 40-40 40l-24 0-31.9 0c-1.5 0-3-.1-4.5-.2c-1.2 .1-2.4 .2-3.6 .2l-16 0c-22.1 0-40-17.9-40-40l0-112c0-.9 0-1.9 .1-2.8l0-69.7-32 0c-18 0-32-14-32-32.1c0-9 3-17 10-24L266.4 8c7-7 15-8 22-8s15 2 21 7L564.8 231.5c8 7 12 15 11 24z"/></svg>

After

Width:  |  Height:  |  Size: 715 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M128 32C92.7 32 64 60.7 64 96l0 256 64 0 0-256 384 0 0 256 64 0 0-256c0-35.3-28.7-64-64-64L128 32zM19.2 384C8.6 384 0 392.6 0 403.2C0 445.6 34.4 480 76.8 480l486.4 0c42.4 0 76.8-34.4 76.8-76.8c0-10.6-8.6-19.2-19.2-19.2L19.2 384z"/></svg>

After

Width:  |  Height:  |  Size: 459 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M0 112.5L0 422.3c0 18 10.1 35 27 41.3c87 32.5 174 10.3 261-11.9c79.8-20.3 159.6-40.7 239.3-18.9c23 6.3 48.7-9.5 48.7-33.4l0-309.9c0-18-10.1-35-27-41.3C462 15.9 375 38.1 288 60.3C208.2 80.6 128.4 100.9 48.7 79.1C25.6 72.8 0 88.6 0 112.5zM128 416l-64 0 0-64c35.3 0 64 28.7 64 64zM64 224l0-64 64 0c0 35.3-28.7 64-64 64zM448 352c0-35.3 28.7-64 64-64l0 64-64 0zm64-192c-35.3 0-64-28.7-64-64l64 0 0 64zM384 256c0 61.9-43 112-96 112s-96-50.1-96-112s43-112 96-112s96 50.1 96 112zM252 208c0 9.7 6.9 17.7 16 19.6l0 48.4-4 0c-11 0-20 9-20 20s9 20 20 20l24 0 24 0c11 0 20-9 20-20s-9-20-20-20l-4 0 0-68c0-11-9-20-20-20l-16 0c-11 0-20 9-20 20z"/></svg>

After

Width:  |  Height:  |  Size: 860 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M280 32c-13.3 0-24 10.7-24 24s10.7 24 24 24l57.7 0 16.4 30.3L256 192l-45.3-45.3c-12-12-28.3-18.7-45.3-18.7L64 128c-17.7 0-32 14.3-32 32l0 32 96 0c88.4 0 160 71.6 160 160c0 11-1.1 21.7-3.2 32l70.4 0c-2.1-10.3-3.2-21-3.2-32c0-52.2 25-98.6 63.7-127.8l15.4 28.6C402.4 276.3 384 312 384 352c0 70.7 57.3 128 128 128s128-57.3 128-128s-57.3-128-128-128c-13.5 0-26.5 2.1-38.7 6L418.2 128l61.8 0c17.7 0 32-14.3 32-32l0-32c0-17.7-14.3-32-32-32l-20.4 0c-7.5 0-14.7 2.6-20.5 7.4L391.7 78.9l-14-26c-7-12.9-20.5-21-35.2-21L280 32zM462.7 311.2l28.2 52.2c6.3 11.7 20.9 16 32.5 9.7s16-20.9 9.7-32.5l-28.2-52.2c2.3-.3 4.7-.4 7.1-.4c35.3 0 64 28.7 64 64s-28.7 64-64 64s-64-28.7-64-64c0-15.5 5.5-29.7 14.7-40.8zM187.3 376c-9.5 23.5-32.5 40-59.3 40c-35.3 0-64-28.7-64-64s28.7-64 64-64c26.9 0 49.9 16.5 59.3 40l66.4 0C242.5 268.8 190.5 224 128 224C57.3 224 0 281.3 0 352s57.3 128 128 128c62.5 0 114.5-44.8 125.8-104l-66.4 0zM128 384a32 32 0 1 0 0-64 32 32 0 1 0 0 64z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M184 48l144 0c4.4 0 8 3.6 8 8l0 40L176 96l0-40c0-4.4 3.6-8 8-8zm-56 8l0 40L64 96C28.7 96 0 124.7 0 160l0 96 192 0 128 0 192 0 0-96c0-35.3-28.7-64-64-64l-64 0 0-40c0-30.9-25.1-56-56-56L184 0c-30.9 0-56 25.1-56 56zM512 288l-192 0 0 32c0 17.7-14.3 32-32 32l-64 0c-17.7 0-32-14.3-32-32l0-32L0 288 0 416c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-128z"/></svg>

After

Width:  |  Height:  |  Size: 584 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M16 64C16 28.7 44.7 0 80 0L304 0c35.3 0 64 28.7 64 64l0 384c0 35.3-28.7 64-64 64L80 512c-35.3 0-64-28.7-64-64L16 64zM224 448a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM304 64L80 64l0 320 224 0 0-320z"/></svg>

After

Width:  |  Height:  |  Size: 423 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M470.7 9.4c3 3.1 5.3 6.6 6.9 10.3s2.4 7.8 2.4 12.2c0 0 0 .1 0 .1c0 0 0 0 0 0l0 96c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-18.7L310.6 214.6c-11.8 11.8-30.8 12.6-43.5 1.7L176 138.1 84.8 216.3c-13.4 11.5-33.6 9.9-45.1-3.5s-9.9-33.6 3.5-45.1l112-96c12-10.3 29.7-10.3 41.7 0l89.5 76.7L370.7 64 352 64c-17.7 0-32-14.3-32-32s14.3-32 32-32l96 0s0 0 0 0c8.8 0 16.8 3.6 22.6 9.3l.1 .1zM0 304c0-26.5 21.5-48 48-48l416 0c26.5 0 48 21.5 48 48l0 160c0 26.5-21.5 48-48 48L48 512c-26.5 0-48-21.5-48-48L0 304zM48 416l0 48 48 0c0-26.5-21.5-48-48-48zM96 304l-48 0 0 48c26.5 0 48-21.5 48-48zM464 416c-26.5 0-48 21.5-48 48l48 0 0-48zM416 304c0 26.5 21.5 48 48 48l0-48-48 0zm-96 80a64 64 0 1 0 -128 0 64 64 0 1 0 128 0z"/></svg>

After

Width:  |  Height:  |  Size: 926 B

+3
View File
@@ -0,0 +1,3 @@
<svg height="30" width="30" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 17 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15"></path>
</svg>

After

Width:  |  Height:  |  Size: 260 B

+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="currentColor" viewBox="0 0 24 24">
<path d="M13.09 3.294c1.924.95 3.422 1.69 5.472.692a1 1 0 0 1 1.438.9v9.54a1 1 0 0 1-.562.9c-2.981 1.45-5.382.24-7.25-.701a38.739 38.739 0 0 0-.622-.31c-1.033-.497-1.887-.812-2.756-.77-.76.036-1.672.357-2.81 1.396V21a1 1 0 1 1-2 0V4.971a1 1 0 0 1 .297-.71c1.522-1.506 2.967-2.185 4.417-2.255 1.407-.068 2.653.453 3.72.967.225.108.443.216.655.32Z"/>
</svg>

After

Width:  |  Height:  |  Size: 463 B

+822
View File
@@ -0,0 +1,822 @@
const getRates = require("../util/exchangeRate");
const { calculateProgress, updatePlanProgressInDB, updateProgress, calculateTotalAssetValue } = require("../util/calculations");
const suggestions = require("../util/suggestions");
const status = require("../util/statuses");
const ObjectId = require('mongodb').ObjectId;
const session = require("express-session");
const bcrypt = require("bcrypt");
const path = require("path");
const joi = require("joi");
const fs = require("fs");
const salt = 12;
/**
* getAssetSchema returns correct joi object
* to validate req.body depending on asset type
* @param {string} type of asset
* @returns {joi.object} schema
*/
const getAssetSchema = (type) => {
let assetSchema;
switch (type) {
case "other":
assetSchema = joi.object({
type: joi.string().valid("other", "stock", "saving").required(),
icon: joi.string().alphanum().required(),
name: joi.string().min(3).max(30).required(),
value: joi.number().min(0).required(),
year: joi.number().min(1900).max(new Date().getFullYear()),
purchaseDate: joi.date().required(),
description: joi.string().max(240).min(0),
id: joi.string().alphanum(), // May be passed when updating existing asset
userId: joi.string().alphanum(),
});
break;
case "saving":
assetSchema = joi.object({
type: joi.string().valid("other", "stock", "saving").required(),
name: joi.string().min(3).max(30).required(),
value: joi.number().min(0).required(),
id: joi.string().alphanum(), // May be passed when updating existing asset
userId: joi.string().alphanum(),
});
break;
case "stock":
assetSchema = joi.object({
type: joi.string().valid("other", "stock", "saving").required(),
ticker: joi.string().min(3).max(5).required(),
price: joi.number().min(0).required(),
quantity: joi.number().min(1).required(),
purchaseDate: joi.date().required(),
id: joi.string().alphanum(), // May be passed when updating existing asset
userId: joi.string().alphanum(),
});
break;
default:
return null;
};
return assetSchema;
}
/**
* Function takes in a number and formats it to currency
* @param {integer}
* @returns nuber formatted in currency
*/
/**
* @param {function} middleware handler
* @param {MongoClient.collection} users db collection
* @param {MongoClient.collection} assets db collection
* @returns {express.Router} user protected routes router
*/
module.exports = (middleware, users, plans, assets) => {
const router = require("express").Router();
// Create list of icon filenames
let icons = fs.readdirSync(path.join(__dirname, "../public/svgs/icons"));
icons = icons.map((icon) => icon.split(".")[0]);
router.use(middleware);
router.get('/home', async (req, res) => {
let user = req.session.user._id;
// if no session with geoData
if (!req.session.geoData) {
req.session.geoData = {
country: null,
toCurrencyRates: [],
};
}
const userPlansFromDB = await plans.find({ userId: new ObjectId(req.session.userId) }).toArray();
for (const plan of userPlansFromDB) {
const progress = await calculateProgress(plan, assets, users, req.session.user._id);
await updatePlanProgressInDB(plan._id, progress.percentage, plans);
}
const updatedUserPlans = await plans.find({ userId: new ObjectId(req.session.user._id) }).toArray();
res.render('dashboard', {
user: req.session.user,
geoData: req.session.geoData,
plans: updatedUserPlans,
});
return res.status(status.Ok);
});
router.get('/assets', async (req, res) => {
if (!req.session.user.financialData || !req.session.user) {
req.session.errMessage = "Please complete your financial data before creating a plan.";
return res.status(status.Unauthorized).redirect('/questionnaire');
}
let userAssets = await assets.find({ userId: new ObjectId(req.session.userId) }).toArray();
let totalUserAssetValue = await calculateTotalAssetValue(userAssets);
res.render('assets', {
user: req.session.user,
errMessage: req.session.errMessage,
assets: userAssets,
geoData: req.session.geoData,
totalUserAssetValue: totalUserAssetValue,
icons: icons,
});
return res.status(status.Ok);
});
router.get('/plans', async (req, res) => {
if (!req.session.user.financialData || !req.session.user) {
req.session.errMessage = "Please complete your financial data before creating a plan.";
return res.status(status.Unauthorized).redirect('/questionnaire');
}
try {
const updatedUserPlans = await updateProgress(plans, assets, users, req.session.user._id);
res.render('plans', {
user: req.session.user,
plans: updatedUserPlans,
geoData: req.session.geoData
});
} catch (err) {
console.error("Error fetching plans:", err);
req.session.errMessage = "Could not load your plans. Please try again.";
res.status(status.InternalServerError).redirect('/home');
}
});
router.get('/plans/:id', async (req, res) => {
try {
const planId = req.params.id;
let userAssets = await assets.find({ userId: new ObjectId(req.session.userId) }).toArray();
let totalUserAssetValue = await calculateTotalAssetValue(userAssets);
if (!ObjectId.isValid(planId)) {
req.session.errMessage = "Invalid plan ID format.";
return res.status(status.BadRequest).redirect('/plans');
}
const plan = await plans.findOne({ userId: new ObjectId(req.session.userId), _id: new ObjectId(planId) });
if (!plan) {
req.session.errMessage = "Plan not found or you do not have permission to view it.";
return res.status(status.NotFound).redirect('/plans');
}
const progress = await calculateProgress(plan, assets, users, req.session.userId);
res.render('planDetail', {
user: req.session.user,
plan: plan,
geoData: req.session.geoData,
totalUserAssetValue: totalUserAssetValue,
assets: userAssets,
progress: progress,
suggestions: await suggestions.generateSuggestions(),
});
} catch (err) {
console.error("Error fetching plan:", err);
req.session.errMessage = "Could not load your plan. Please try again.";
res.status(status.InternalServerError).redirect('/home');
}
});
// router.get('/newPlan', (req, res) => {
// if (!req.session.user.financialData || !req.session.user) {
// req.session.errMessage = "Please complete your financial data before creating a plan.";
// return res.status(status.Unauthorized).redirect('/questionnaire');
// }
// const errMessage = req.session.errMessage;
// req.session.errMessage = "";
// res.render('newPlan', {
// user: req.session.user,
// errMessage: errMessage,
// geoData: req.session.geoData
// });
// });
router.post('/newPlan', async (req, res) => {
if (!req.session.user.financialData || !req.session.user) {
req.session.errMessage = "Please complete your financial data before creating a plan.";
return res.status(status.Unauthorized).redirect('/questionnaire');
}
const planSchema = joi.object({
name: joi.string().min(3).max(100).required(),
retirementAge: joi.number().min(18).max(120).required(),
retirementExpenses: joi.number().min(0).required(),
retirementAssets: joi.number().min(0).required(),
retirementLiabilities: joi.number().min(0).required(),
});
const validationOptions = { convert: true, abortEarly: false };
const { error, value } = planSchema.validate(req.body, validationOptions);
if (error) {
console.error("Plan validation error:", error.details);
const errorMessage = "Invalid input: " + error.details.map(d => d.message.replace(/"/g, '')).join(', ');
res.status(status.BadRequest).json({ success: false, message: errorMessage });
return;
}
const planToInsert = {
userId: new ObjectId(req.session.userId),
name: value.name,
retirementAge: value.retirementAge,
retirementExpenses: value.retirementExpenses,
retirementAssets: value.retirementAssets,
retirementLiabilities: value.retirementLiabilities,
progress: "0"
};
try {
await plans.insertOne(planToInsert);
res.status(status.Ok).json({ success: true, message: "Plan created successfully." });
}
catch (err) {
console.error("Error saving plan:", err);
res.status(status.InternalServerError).json({ success: false, message: "An error occurred while saving your plan. Please try again." });
}
});
router.delete('/plans/:id', async (req, res) => {
const planId = req.params.id;
const plan = await plans.findOne({ _id: new ObjectId(planId) });
if(!plan) {
console.log(`Plan not found with ID: ${planId}`);
res.status(status.NotFound).json({ success: false, message: "Plan not found or you do not have permission to delete it." });
return;
}
if(plan.userId.toString() !== req.session.userId) {
console.log(`User ${req.session.userId} does not have permission to delete plan ${planId}`);
res.status(status.Forbidden).json({ success: false, message: "You do not have permission to delete this plan." });
return;
}
try {
await plans.deleteOne({ _id: new ObjectId(planId) });
res.status(status.Ok).json({ success: true, message: "Plan deleted successfully." });
}
catch (err) {
console.error("Error deleting plan:", err);
res.status(status.InternalServerError).json({ success: false, message: "An error occurred while deleting your plan. Please try again." });
}
});
router.get('/plans/:id/edit', async (req, res) => {
const planId = req.params.id;
const plan = await plans.findOne({ _id: new ObjectId(planId) });
if(!plan) {
console.log(`Plan not found with ID: ${planId}`);
res.status(status.NotFound).json({ success: false, message: "Plan not found or you do not have permission to edit it." });
return;
}
if(plan.userId.toString() !== req.session.userId) {
console.log(`User ${req.session.userId} does not have permission to edit plan ${planId}`);
res.status(status.Forbidden).json({ success: false, message: "You do not have permission to edit this plan." });
return;
}
res.render('editPlan', {
user: req.session.user,
plan: plan,
geoData: req.session.geoData
});
});
router.post('/plans/:id/edit', async (req, res) => {
const planId = req.params.id;
const plan = await plans.findOne({ _id: new ObjectId(planId) });
if(!plan) {
console.log(`Plan not found with ID: ${planId}`);
res.status(status.NotFound).json({ success: false, message: "Plan not found or you do not have permission to edit it." });
return;
}
if(plan.userId.toString() !== req.session.userId) {
console.log(`User ${req.session.userId} does not have permission to edit plan ${planId}`);
res.status(status.Forbidden).json({ success: false, message: "You do not have permission to edit this plan." });
return;
}
const planSchema = joi.object({
name: joi.string().min(3).max(100).required(),
retirementAge: joi.number().min(18).max(120).required(),
retirementExpenses: joi.number().min(0).required(),
retirementAssets: joi.number().min(0).required(),
retirementLiabilities: joi.number().min(0).required(),
});
// Object for Joi validation - only fields from req.body
const dataToValidate = {
name: req.body.name,
retirementAge: Number(req.body.retirementAge),
retirementExpenses: parseFloat(req.body.retirementExpenses),
retirementAssets: parseFloat(req.body.retirementAssets),
retirementLiabilities: parseFloat(req.body.retirementLiabilities),
};
const { error, value } = planSchema.validate(dataToValidate);
if (error) {
console.error("Plan validation error:", error.details);
const errorMessage = "Invalid input: " + error.details.map(d => d.message.replace(/"/g, '')).join(', ');
res.status(status.BadRequest).json({ success: false, message: errorMessage });
return;
}
const progressCalculated = await calculateProgress(value, assets, users, req.session.userId);
const planToSet = {
name: value.name,
retirementAge: value.retirementAge,
retirementExpenses: value.retirementExpenses,
retirementAssets: value.retirementAssets,
retirementLiabilities: value.retirementLiabilities,
progress: progressCalculated.percentage,
};
try {
await plans.updateOne(
{ _id: new ObjectId(planId), userId: new ObjectId(req.session.userId) },
{ $set: planToSet }
);
await updateProgress(plans, assets, users, req.session.user._id);
res.status(status.Ok).json({ success: true, message: "Plan updated successfully." });
}
catch (err) {
console.error("Error updating plan:", err);
res.status(status.InternalServerError).json({ success: false, message: "An error occurred while updating your plan. Please try again." });
}
});
router.post('/fact', async (req, res) => {
const factInput = req.body.fact;
const fact = await suggestions.generateFact(factInput);
return res.status(status.Ok).json({ fact });
});
router.get('/more', (req, res) => {
res.render('more', {
user: req.session.user,
geoData: req.session.geoData
});
return res.status(status.Ok);
});
router.get('/profile', (req, res) => {
res.render('profile', {
user: req.session.user,
errMessage: req.session.errMessage,
geoData: req.session.geoData
});
return res.status(status.Ok);
});
router.get('/settings', (req, res) => {
res.render('settings', {
user: req.session.user,
geoData: req.session.geoData
});
return res.status(status.Ok);
});
router.get('/questionnaire', (req, res) => {
const errMessage = req.session.errMessage;
req.session.errMessage = "";
res.render('questionnaire', {
user: req.session.user,
errMessage: errMessage,
geoData: req.session.geoData
});
});
router.post('/questionnaire', (req, res) => {
const questionnaireSchema = joi.object({
dob: joi.date().required(),
education: joi.string().valid('primary', 'secondary', 'tertiary', 'postgraduate').required(),
maritalStatus: joi.string().valid('single', 'married', 'divorced', 'widowed').required(),
income: joi.number().min(0).required(),
expenses: joi.number().min(0).required(),
assets: joi.number().min(0).required(),
liabilities: joi.number().min(0).required(),
});
const validationOptions = { convert: true, abortEarly: false };
const { error, value } = questionnaireSchema.validate(req.body, validationOptions);
let referrer = req.get('Referrer') || "/home";
if (error) {
console.error("Questionnaire validation error:", error.details);
req.session.errMessage = "Invalid input: " + error.details.map(d => d.message.replace(/"/g, '')).join(', ');
let redirect = referrer.includes("?profile") ? "/questionnaire?profile" : "/questionnaire";
res.status(status.BadRequest).redirect(redirect);
return;
}
users.updateOne(
{ _id: new ObjectId(req.session.userId) },
{
$set: {
financialData: true,
dob: value.dob,
education: value.education,
maritalStatus: value.maritalStatus,
income: value.income,
expenses: value.expenses,
assets: value.assets,
liabilities: value.liabilities,
}
}
).then((result) => {
if (result.matchedCount === 0) {
console.log(`User not found during questionnaire update: ${req.session.userId}`);
req.session.errMessage = "User session invalid. Please log in again.";
res.status(status.NotFound).redirect("/login");
return;
}
if (result.modifiedCount === 0 && result.matchedCount === 1) {
console.log(`User questionnaire data unchanged (already up-to-date): ${req.session.userId}`);
}
req.session.errMessage = "";
req.session.user = null; // set user to null so middleware updates user
req.session.save((err) => {
if (err) {
console.error("Failed to save session: ", err);
return req.session.destroy((err) => {
req.session.errMessage = "Failed to save session, please login again.";
if (err) {
console.error("Failed to destroy session: ", err);
}
res.status(status.InternalServerError);
return res.redirect("/login");
});
}
let redirect = referrer.includes("?profile") ? "/profile" :
referrer != "/home" ? "/plans" : referrer;
return res.status(status.Ok).redirect(redirect);
});
}).catch(err => {
console.error("Error updating questionnaire in database:", err);
req.session.errMessage = "An error occurred while saving your information. Please try again.";
let redirect = referrer.includes("?profile") ? "/questionnaire?profile" : "/questionnaire";
res.status(status.InternalServerError).redirect(redirect);
});
});
router.post("/updateAccount", async (req, res) => {
const accountSchema = joi.object({
email: joi.string().email({ minDomainSegments: 2, tlds: { allow: true } }),
name: joi.string().pattern(new RegExp('^[a-zA-Z0-9_ ]*$')).max(20),
password: joi.string().max(20).min(8),
repassword: joi.string().max(20).min(8),
});
const valid = accountSchema.validate(req.body);
if (valid.error) {
req.session.errMessage = "Invalid input:" + valid.error.details.map(d => d.message.replace(/"/g, '')).join(', ');
res.status(status.BadRequest);
return res.redirect("/profile");
}
let update = {
email: req.body.email,
name: req.body.name,
};
if ((req.body.password != "") && (req.body.password != req.body.repassword)) {
req.session.errMessage = "New passwords must match";
res.status(status.BadRequest);
return res.redirect("/profile");
} else if (req.body.password != "") {
let hashedPassword = await bcrypt.hashSync(req.body.password, salt);
update.password = hashedPassword;
}
users.updateOne(
{ _id: new ObjectId(req.session.userId) },
{ $set: update }
).then((result) => {
if (result.matchedCount === 0) {
console.log(`User not found during account update: ${req.session.userId}`);
req.session.errMessage = "User session invalid. Please log in again.";
res.status(status.NotFound).redirect("/login");
return;
}
if (result.modifiedCount === 0 && result.matchedCount === 1) {
console.log(`User account data unchanged (already up-to-date): ${req.session.userId}`);
}
req.session.errMessage = "";
req.session.user = null; // set user to null so middleware updates user
req.session.save((err) => {
if (err) {
console.error("Failed to save session: ", err);
return req.session.destroy((err) => {
req.session.errMessage = "Failed to save session, please login again.";
if (err) {
console.error("Failed to destroy session: ", err);
}
res.status(status.InternalServerError);
return res.redirect("/login");
});
}
return res.status(status.Ok).redirect("/profile");
});
}).catch(err => {
console.error("Error updating account in database:", err);
req.session.errMessage = "An error occurred while saving your information. Please try again.";
return res.status(status.InternalServerError).redirect("/profile");
});
});
router.post("/updatePersonal", (req, res) => {
const questionnaireSchema = joi.object({
dob: joi.date().required(),
education: joi.string().valid('primary', 'secondary', 'tertiary', 'postgraduate').required(),
maritalStatus: joi.string().valid('single', 'married', 'divorced', 'widowed').required(),
income: joi.number().min(0).required(),
expenses: joi.number().min(0).required(),
assets: joi.number().min(0).required(),
liabilities: joi.number().min(0).required(),
});
const validationOptions = { convert: true, abortEarly: false };
const { error, value } = questionnaireSchema.validate(req.body, validationOptions);
if (error) {
console.error("Personal info validation error:", error.details);
req.session.errMessage = "Invalid input: " + error.details.map(d => d.message.replace(/"/g, '')).join(', ');
res.status(status.BadRequest).redirect("/profile");
return;
}
users.updateOne(
{ _id: new ObjectId(req.session.userId) },
{
$set: {
financialData: true,
dob: value.dob,
education: value.education,
maritalStatus: value.maritalStatus,
income: value.income,
expenses: value.expenses,
assets: value.assets,
liabilities: value.liabilities,
}
}
).then((result) => {
if (result.matchedCount === 0) {
console.log(`User not found during personal info update: ${req.session.userId}`);
req.session.errMessage = "User session invalid. Please log in again.";
res.status(status.NotFound).redirect("/login");
return;
}
if (result.modifiedCount === 0 && result.matchedCount === 1) {
console.log(`User personal info unchanged (already up-to-date): ${req.session.userId}`);
}
req.session.errMessage = "";
req.session.user = null; // set user to null so middleware updates user
req.session.save((err) => {
if (err) {
console.error("Failed to save session: ", err);
return req.session.destroy((err) => {
req.session.errMessage = "Failed to save session, please login again.";
if (err) {
console.error("Failed to destroy session: ", err);
}
res.status(status.InternalServerError);
return res.redirect("/login");
});
}
return res.status(status.Ok).redirect("/profile");
});
}).catch(err => {
console.error("Error updating personal info in database:", err);
req.session.errMessage = "An error occurred while saving your information. Please try again.";
res.status(status.InternalServerError).redirect("/profile");
});
});
router.post("/createAsset", (req, res) => {
// Create asset, each asset has different data structure based on type
const type = req.body.type;
const assetSchema = getAssetSchema(type);
if (!assetSchema) {
console.error("Modified asset type, rejected");
req.session.errMessage = "Invalid input";
return res.status(status.BadRequest).redirect("/assets");
}
const valid = assetSchema.validate(req.body);
if (valid.error) {
req.session.errMessage = "Invalid input:" + valid.error.details.map(d => d.message.replace(/"/g, '')).join(', ');
res.status(status.BadRequest);
return res.redirect("/assets");
}
let newAsset = {
userId: new ObjectId(req.session.userId),
...req.body,
updatedAt: new Date(),
};
if (type == "stock") {
newAsset.quantity = parseInt(newAsset.quantity);
newAsset.price = parseFloat(newAsset.price)
newAsset.value = newAsset.quantity * newAsset.price;
newAsset.name = `${newAsset.ticker} Stock`;
}
if (type == "other" && newAsset.year != "") newAsset.year = parseInt(newAsset.year);
newAsset.value = parseFloat(newAsset.value);
newAsset.icon = type == "stock" ? "Stock" : type == "saving" ? "Coins" : newAsset.icon;
assets.insertOne(newAsset, (err, _) => {
if (err) {
console.error("Error creating asset: ", err);
req.session.errMessage = "Internal server error";
return res.status(status.InternalServerError).redirect("/assets");
}
});
req.session.errMessage = "";
return res.status(status.Ok).redirect("/assets");
});
router.post("/updateAsset", (req, res) => {
const type = req.body.type;
const assetSchema = getAssetSchema(type);
if (!assetSchema) {
console.error("Modified asset type, rejected");
req.session.errMessage = "Invalid input";
return res.status(status.BadRequest).redirect("/assets");
}
const valid = assetSchema.validate(req.body);
if (valid.error) {
req.session.errMessage = "Invalid input:" + valid.error.details.map(d => d.message.replace(/"/g, '')).join(', ');
res.status(status.BadRequest);
return res.redirect("/assets");
}
if (req.body.userId != req.session.userId) {
req.session.errMessage = "Cannot change asset owner",
res.status(status.BadRequest);
return res.redirect("/assets");
}
let update = { ...req.body, updatedAt: new Date() };
if (type == "stock") {
update.quantity = parseInt(update.quantity);
update.price = parseFloat(update.price)
update.value = update.quantity * update.price;
update.name = `${update.ticker} Stock`;
}
if (type == "other" && update.year != "") update.year = parseInt(update.year);
update.value = parseFloat(update.value);
delete update.id;
delete update.userId;
assets.updateOne(
{ "_id": new ObjectId(req.body.id) },
{ $set: update },
).then((result) => {
if (result.matchedCount === 0) {
console.log(`Asset not found: ${req.body.id}`);
req.session.errMessage = "Unable to update asset";
return res.status(status.NotFound).redirect("/assets");
}
if (result.modifiedCount === 0 && result.matchedCount === 1) {
console.log(`Asset data unchanged (already up-to-date): ${req.body.id}`);
}
req.session.errMessage = "";
return res.status(status.Ok).redirect("/assets");
}).catch((err) => {
console.error("Error updating asset: ", err);
req.session.errMessage = "An error occurred while saving your information. Please try again.";
return res.status(status.InternalServerError).redirect("/assets");
});
});
router.post("/deleteAsset", (req, res) => {
const id = new ObjectId(req.body.id);
assets.deleteOne(
{ "_id": id }
).then((result) => {
if (result.deletedCount === 0) {
console.error(`Asset not found: ${req.body.id}`);
req.session.errMessage = "Unable to delete asset. Please try again.";
return res.status(status.NotFound).redirect("/assets");
}
if (!result.acknowledged) {
console.error("Error deleting asset: ", err);
req.session.errMessage = "An error occurred while deleting an asset. Please try again.";
return res.status(status.InternalServerError).redirect("/assets");
}
req.session.errMessage = "";
return res.status(status.Ok).redirect("/assets");
}).catch((err) => {
console.error("Error deleting asset: ", err);
req.session.errMessage = "An error occurred while deleting an asset. Please try again.";
return res.status(status.InternalServerError).redirect("/assets");
});
});
router.post("/deleteUser", (req, res) => {
// not as critical if results aren't as expected only if crashing
assets.deleteMany({ userId: new ObjectId(req.session.userId) }).catch((err) => {
console.error("Error deleting user assets: ", err);
req.session.errMessage = "An error occured while deleting your account. Please try again.";
return res.status(status.InternalServerError).redirect("/profile");
});
plans.deleteMany({ userId: new ObjectId(req.session.userId) }).catch((err) => {
console.error("Error deleting user assets: ", err);
req.session.errMessage = "An error occured while deleting your account. Please try again.";
return res.status(status.InternalServerError).redirect("/profile");
});
users.deleteOne(
{ _id: new ObjectId(req.session.userId) },
).then((result) => {
if (result.deletedCount === 0) {
console.error(`User not found: ${req.body.id}`);
req.session.errMessage = "Unabled to delete account. Please try again.";
return res.status(status.NotFound).redirect("/profile");
}
if (!result.acknowledged) {
console.error("Error deleting user: ", err);
req.session.errMessage = "An error occured while deleting your account. Please try again.";
return res.status(status.InternalServerError).redirect("/profile");
}
// Direct to logout to destroy session
req.session.destroy();
return res.status(status.Ok).redirect("/signup");
}).catch((err) => {
console.error("Error deleting user: ", err);
req.session.errMessage = "An error occured while deleting your account. Please try again.";
return res.status(status.InternalServerError).redirect("/profile");
});
});
router.get("/exRates/:lat/:lon", async (req, res) => {
if (!req.session.geoData.country) {
const response = await fetch(`https://maps.googleapis.com/maps/api/geocode/json?latlng=${req.params.lat},${req.params.lon}&result_type=country&key=${process.env.GEOLOCATION_API}`);
const data = await response.json();
country = data.results[0].formatted_address;
let results = await getRates(country);
if (!results.exRates) {
req.session.geoData = {
message: "error"
}
} else {
req.session.geoData = {
country: results.abbreviation,
toCurrencyRates: results.exRates,
geoData: req.session.geoData
}
}
}
return res.status(status.Ok).send({ data: req.session.geoData });
})
return router;
};
+174
View File
@@ -0,0 +1,174 @@
const ObjectId = require('mongodb').ObjectId;
async function calculatePlanProgress(plans, assets, userId) {
if (!plans || typeof plans.retirementAssets === 'undefined') {
console.error("Invalid plan document provided to calculatePlanProgress:", plans);
return 0;
}
if (!assets || typeof assets.find !== 'function') {
console.error("Invalid assetsCollection provided to calculatePlanProgress");
return 0;
}
try {
const userAssets = await assets.find({ userId: new ObjectId(userId) }).toArray();
const totalUserAssetValue = await calculateTotalAssetValue(userAssets);
let percentage = 0;
if (plans.retirementAssets > 0) {
percentage = (totalUserAssetValue / plans.retirementAssets) * 100;
}
return percentage;
} catch (err) {
console.error("Error in calculatePlanProgress:", err);
return 0;
}
}
async function updatePlanProgressInDB(planId, percentage, plans) {
if (!plans || typeof plans.updateOne !== 'function') {
console.error("Error with the plans collection");
return;
}
if (typeof percentage !== 'number' || isNaN(percentage)) {
console.error(`Error with the percentage: ${percentage}`);
return;
}
try {
await plans.updateOne({ _id: new ObjectId(planId) }, { $set: { progress: parseFloat(percentage.toFixed(2)) } });
} catch (err) {
console.error("Error in updatePlanProgressInDB:", err);
}
}
async function calculateTotalAssetValue(assets) {
if (!assets || typeof assets.find !== 'function') {
console.error("Error with the assets collection");
return 0;
}
try {
let totalAssetValue = 0;
const today = new Date();
for (const asset of assets) {
if (asset.icon === "Motorcycle" || asset.icon === "Car") {
if (asset.value < 1000) {
totalAssetValue += asset.value;
} else {
const assetYear = asset.year;
const age = today.getFullYear() - assetYear;
let depreciatedValue;
if (age < 1){
depreciatedValue = asset.value * 0.9;
}else{
depreciatedValue = asset.value * Math.pow(0.90, age);
}
if (depreciatedValue < 1000) {
totalAssetValue += 1000;
} else {
totalAssetValue += depreciatedValue;
}
}
} else {
totalAssetValue += asset.value;
}
}
return totalAssetValue;
} catch (err) {
console.error("Error in calculateTotalAssetValue:", err);
return 0;
}
}
async function calculateProgress(plan, assets, users, userId) {
if (!plan || typeof plan !== 'object') {
console.error("Error with the plan");
return;
}
if (!assets || typeof assets.find !== 'function') {
console.error("Error with the assets collection");
return;
}
if (!users || typeof users.findOne !== 'function') {
console.error("Error with the users collection");
return;
}
if (!userId) {
console.error("No user ID provided");
return;
}
try {
const userAssets = await assets.find({ userId: new ObjectId(userId) }).toArray();
const totalUserAssetValue = await calculateTotalAssetValue(userAssets);
const totalUserPlanValue = plan.retirementAssets;
const userDoc = await users.findOne({ _id: new ObjectId(userId) });
if (!userDoc || !userDoc.dob) {
console.error("[calculateProgress] User document or DOB not found for userId:", userId);
// Ensure a structured return even on error to avoid undefined.progress issues
return { monthlyInvestment: NaN, totalCostOfRetirement: NaN, monthsUntilRetirement: NaN, yearsRetired: NaN, yearsUntilRetirement: NaN, percentage: NaN };
}
const userDob = new Date(userDoc.dob);
if (isNaN(userDob.getTime())) {
console.error("[calculateProgress] userDob is an invalid date. Aborting calculation.");
return { monthlyInvestment: NaN, totalCostOfRetirement: NaN, monthsUntilRetirement: NaN, yearsRetired: NaN, yearsUntilRetirement: NaN, percentage: NaN };
}
const today = new Date();
const userUnalivedBy = new Date(userDob);
userUnalivedBy.setFullYear(userUnalivedBy.getFullYear() + 90);
const yearOfRetirement = userDob.getFullYear() + plan.retirementAge;
const monthsUntilRetirement = (yearOfRetirement - today.getFullYear()) * 12;
const yearsRetired = userUnalivedBy.getFullYear() - yearOfRetirement;
const totalCostOfRetirement = ((plan.retirementExpenses + plan.retirementLiabilities) * 12) * yearsRetired;
const monthlyInvestment = (totalUserPlanValue - totalUserAssetValue + totalCostOfRetirement) / monthsUntilRetirement;
const percentageCalculated = (totalUserAssetValue / (totalUserPlanValue + totalCostOfRetirement)) * 100;
if (monthsUntilRetirement <= 0) {
return { monthlyInvestment: 0, totalCostOfRetirement: totalCostOfRetirement, monthsUntilRetirement: 0, yearsRetired: yearsRetired, yearsUntilRetirement: 0, percentage: 0 };
}
const progress = {};
progress.monthlyInvestment = Math.round(monthlyInvestment);
progress.totalCostOfRetirement = totalCostOfRetirement;
progress.monthsUntilRetirement = monthsUntilRetirement;
progress.yearsRetired = yearsRetired;
progress.yearsUntilRetirement = (yearOfRetirement - today.getFullYear());
progress.percentage = percentageCalculated;
return progress;
} catch (err) {
console.error("Error in calculateProgress:", err);
// Ensure a structured return even on error to avoid undefined.progress issues
return { monthlyInvestment: NaN, totalCostOfRetirement: NaN, monthsUntilRetirement: NaN, yearsRetired: NaN, yearsUntilRetirement: NaN, percentage: NaN };
}
}
async function updateProgress(plans, assets, users, userId) {
const userPlansFromDB = await plans.find({ userId: new ObjectId(userId) }).toArray();
for (const plan of userPlansFromDB) {
const progress = await calculateProgress(plan, assets, users, userId);
await updatePlanProgressInDB(plan._id, progress.percentage, plans);
}
const updatedUserPlans = await plans.find({ userId: new ObjectId(userId) }).toArray();
return updatedUserPlans;
}
module.exports = { calculatePlanProgress, updatePlanProgressInDB, calculateTotalAssetValue, calculateProgress, updateProgress};
+132
View File
@@ -0,0 +1,132 @@
{
"countries": [
{
"country": "United States",
"currency_abbreviation": "USD"
},
{
"country": "Eurozone",
"currency_abbreviation": "EUR"
},
{
"country": "United Kingdom",
"currency_abbreviation": "GBP"
},
{
"country": "Japan",
"currency_abbreviation": "JPY"
},
{
"country": "Switzerland",
"currency_abbreviation": "CHF"
},
{
"country": "Canada",
"currency_abbreviation": "CAD"
},
{
"country": "Australia",
"currency_abbreviation": "AUD"
},
{
"country": "China",
"currency_abbreviation": "CNY"
},
{
"country": "India",
"currency_abbreviation": "INR"
},
{
"country": "Russia",
"currency_abbreviation": "RUB"
},
{
"country": "Brazil",
"currency_abbreviation": "BRL"
},
{
"country": "Mexico",
"currency_abbreviation": "MXN"
},
{
"country": "South Korea",
"currency_abbreviation": "KRW"
},
{
"country": "Indonesia",
"currency_abbreviation": "IDR"
},
{
"country": "Bulgaria",
"currency_abbreviation": "BGN"
},
{
"country": "Czech Republic",
"currency_abbreviation": "CZK"
},
{
"country": "Denmark",
"currency_abbreviation": "DKK"
},
{
"country": "Hong Kong",
"currency_abbreviation": "HKD"
},
{
"country": "Hungary",
"currency_abbreviation": "HUF"
},
{
"country": "Israel",
"currency_abbreviation": "ILS"
},
{
"country": "Iceland",
"currency_abbreviation": "ISK"
},
{
"country": "Malaysia",
"currency_abbreviation": "MYR"
},
{
"country": "Norway",
"currency_abbreviation": "NOK"
},
{
"country": "New Zealand",
"currency_abbreviation": "NZD"
},
{
"country": "Philippines",
"currency_abbreviation": "PHP"
},
{
"country": "Poland",
"currency_abbreviation": "PLN"
},
{
"country": "Romania",
"currency_abbreviation": "RON"
},
{
"country": "Sweden",
"currency_abbreviation": "SEK"
},
{
"country": "Singapore",
"currency_abbreviation": "SGD"
},
{
"country": "Thailand",
"currency_abbreviation": "THB"
},
{
"country": "Turkey",
"currency_abbreviation": "TRY"
},
{
"country": "South Africa",
"currency_abbreviation": "ZAR"
}
]
}
+18
View File
@@ -0,0 +1,18 @@
const countries = require("./countries.json").countries;
async function getRates(country) {
let abbr;
countries.forEach(c => {
if (c.country === country) {
abbr = c.currency_abbreviation;
}
});
const res = await fetch(`https://api.frankfurter.dev/v1/latest?base=${abbr}`);
const data = await res.json();
let rates = data.rates;
return { exRates: rates, abbreviation: abbr };
}
module.exports = getRates;
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
Ok: 200,
BadRequest: 400,
Unauthorized: 401,
NotFound: 404,
InternalServerError: 500,
};
+24
View File
@@ -0,0 +1,24 @@
const { GoogleGenAI } = require("@google/genai");
const ai = new GoogleGenAI({ apiKey: process.env.GOOGLE_API_KEY });
async function generateFact(factInput) {
const response = await ai.models.generateContent({
model: "gemini-2.0-flash",
contents: `Generate a concise investment fact about "${factInput}". If "${factInput}" is not directly investment-related, provide a general, useful investment fact instead. Deliver only the fact itself, with no extra text or explanation.`,
});
return response.text;
}
async function generateSuggestions() {
const response = await ai.models.generateContent({
model: "gemini-2.0-flash",
contents: "Return a mock investment suggestion 3-5 lines only",
});
return response.text;
}
module.exports = {
generateFact,
generateSuggestions
};
+25 -53
View File
@@ -1,53 +1,24 @@
<html> <%- include("./partials/fileHeader") %>
<head>
<title>RCalculator</title>
<link rel="icon" href="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/rekor.png">
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@3.3.2/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="bg-white dark:bg-gray-900">
<header>
<nav class="bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
<a href="#" class="flex items-center space-x-3 rtl:space-x-reverse">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/rekor.png" class="h-8" alt="Flowbite Logo" />
<span class="self-center text-2xl font-semibold whitespace-nowrap dark:text-white">RCalculator</span>
</a>
<button data-collapse-toggle="navbar-solid-bg" type="button" class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600" aria-controls="navbar-solid-bg" aria-expanded="false">
<span class="sr-only">Open main menu</span>
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15"/>
</svg>
</button>
<div class="hidden w-full md:block md:w-auto" id="navbar-solid-bg">
<ul class="flex flex-col font-medium mt-4 rounded-lg bg-gray-50 md:space-x-8 rtl:space-x-reverse md:flex-row md:mt-0 md:border-0 md:bg-transparent dark:bg-gray-800 md:dark:bg-transparent dark:border-gray-700">
<li>
<a href="/landing" class="block py-2 px-3 md:p-0 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent">Home</a>
</li>
<li>
<a href="/aboutUs" class="block py-2 px-3 md:p-0 text-white bg-blue-700 rounded md:bg-transparent md:text-blue-700 md:dark:text-blue-500 dark:bg-blue-600 md:dark:bg-transparent" aria-current="page">About Us</a>
</li>
<li>
<a href="mailto:rcalculator@gmail.com" class="block py-2 px-3 md:p-0 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent">Contact us</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<main>
<section class="bg-white dark:bg-gray-900 py-12 md:py-20"> <% if (!geoData) { %>
<%- include("./partials/headerStart") %>
<% } else { %>
<%- include("./partials/header") %>
<% } %>
<main class="pt-24 pb-16">
<section class="bg-gray-900 py-12 md:py-20">
<div class="max-w-screen-lg mx-auto px-4 text-center"> <div class="max-w-screen-lg mx-auto px-4 text-center">
<h1 class="mb-4 text-4xl font-extrabold tracking-tight leading-none text-gray-900 md:text-5xl lg:text-6xl dark:text-white">About RCalculator</h1> <h1 class="mb-4 text-4xl font-extrabold tracking-tight leading-none md:text-5xl lg:text-6xl text-white">About RCalculator</h1>
<p class="mb-12 text-lg font-normal text-gray-500 lg:text-xl sm:px-16 dark:text-gray-400">Welcome to RCalculator, your partner in building a secure and fulfilling financial future.</p> <p class="mb-12 text-lg font-normal lg:text-xl sm:px-16 text-gray-400">Welcome to RCalculator, your partner in building a secure and fulfilling financial future.</p>
</div> </div>
<div class="max-w-screen-md mx-auto px-4"> <div class="max-w-screen-md mx-auto px-4">
<h2 class="mb-4 text-3xl font-bold tracking-tight text-gray-900 dark:text-white text-center">Our Mission</h2> <h2 class="mb-4 text-3xl font-bold tracking-tight text-white text-center">Our Mission</h2>
<p class="mb-6 font-normal text-gray-600 dark:text-gray-400 text-lg text-center"> <p class="mb-6 font-normal text-gray-400 text-lg text-center">
Our mission is to empower you with the tools and insights needed to take control of your long-term financial goals. Our mission is to empower you with the tools and insights needed to take control of your long-term financial goals.
</p> </p>
<div class="prose lg:prose-lg dark:prose-invert mx-auto text-gray-600 dark:text-gray-400"> <div class="prose lg:prose-lg mx-auto text-gray-400">
<p class="mb-4"> <p class="mb-4">
We believe that planning for your desired future, especially retirement, shouldn't be daunting. It doesn't matter what your current age or career status is starting now is what counts. We believe that planning for your desired future, especially retirement, shouldn't be daunting. It doesn't matter what your current age or career status is starting now is what counts.
</p> </p>
@@ -59,22 +30,23 @@
</p> </p>
</div> </div>
</div> </div>
<% if (!geoData) { %>
<div class="flex flex-col space-y-4 sm:flex-row sm:justify-center sm:space-y-0"> <div class="flex flex-col space-y-4 sm:flex-row sm:justify-center sm:space-y-0">
<a href="/login" class="inline-flex justify-center items-center mt-4 py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 dark:focus:ring-blue-900"> <a href="/signup" class="inline-flex justify-center items-center mt-4 py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-900">
Get started Get started
<svg class="w-3.5 h-3.5 ms-2 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 10"> <svg class="w-3.5 h-3.5 ms-2 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 10">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 5h12m0 0L9 1m4 4L9 9"/> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 5h12m0 0L9 1m4 4L9 9"/>
</svg> </svg>
</a> </a>
</div> </div>
<% } %>
</section> </section>
</main> </main>
<footer class="bg-gray-100 dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 p-6 text-center text-gray-500 dark:text-gray-400 text-sm">
<div class="container mx-auto"> <% if (!geoData) { %>
<p>&copy; 2025 RCalculator. All rights reserved.</p> <% } else { %>
</div> <%- include("./partials/navBar") %>
</footer> <% } %>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.3.0/flowbite.min.js"></script> <%- include("./partials/footer") %>
</body>
</html>
+75
View File
@@ -0,0 +1,75 @@
<%- include("./partials/fileHeader") %>
<%- include("./partials/header") %>
<main class="pt-26">
<%- include("./partials/createAsset", { icons: icons }) %>
<% if (errMessage != "") { %>
<div class="max-w-md mx-auto mb-10 rounded-lg shadow-md text-white font-bold bg-red-400 py-4 text-center"><%= errMessage %></div>
<% } %>
<!-- Main display -->
<section>
<div class="mt-2 max-w-md mx-auto bg-white px-8 py-6 rounded-lg shadow-md flex flex-col justify-center">
<div class="flex flex-row justify-between text-right">
<h1 class="text-lg font-medium">Assets:</h1>
<h1 class="text-lg font-medium"><%= assets.length %></h1>
</div>
<div class="flex flex-row justify-between text-right mt-6">
<h1 class="text-lg font-medium">Total Value:</h1>
<h1 class="text-lg font-medium">
<!-- sum of assets array values and formated as proper dollar amount -->
<%- new Intl.NumberFormat('en-US', { style: 'currency', currency: geoData.currency ? geoData.currency : 'CAD' }).format(totalUserAssetValue) %>
</h1>
</div>
</div>
<div class="w-md my-6 max-w-md mx-auto px-4 flex flex-row justify-between">
<a href="/home" class="text-center w-sm py-2 mx-2 px-4 border
border-transparent rounded-md shadow-sm text-sm font-medium
text-white bg-blue-600 hover:bg-blue-700 focus:outline-none">
Back to Dashboard
</a>
<button
class="text-center w-sm py-2 py-2 mx-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600
hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 cursor-pointer"
onclick="document.getElementById('create-asset-modal').showModal()"
>
Add New Asset
</button>
</div>
</section>
<hr class="h-px my-8 bg-gray-500 border-0 mx-10">
<!-- list each asset and create hidden popup modal for view/edit -->
<section class="mt-6">
<% if (assets.length == 0) { %>
<div class="max-w-md mx-auto mb-10 rounded-lg shadow-md text-white font-bold bg-red-400 py-4 text-center">
You have no assets, create a new asset.
</div>
<% } %>
<% assets.forEach((asset) => { %>
<%- include("./partials/assetPreview", { asset: asset }); %>
<%- include("./partials/assetDelete", { asset: asset }); %>
<%- include("./partials/assetModify", { asset: asset }); %>
<% }) %>
</section>
</main>
<div class="mt-20"></div>
<script src="/static/scripts/assetManager.js"></script>
<script>
// Ensure all assets popup modals are locked and reset on page load
let assets = <%- JSON.stringify(assets) %>;
assets.forEach((asset) => {
lockAsset(asset._id, asset.icon);
});
</script>
<%- include("./partials/navBar") %>
<%- include("./partials/scriptLoader") %>
<%- include("./partials/footer") %>
+43
View File
@@ -0,0 +1,43 @@
<%- include("./partials/fileHeader") %>
<%- include("./partials/header") %>
<main class="pt-24 pb-14">
<!-- Buttons -->
<div class="flex justify-end gap-4 px-5 py-6">
<a href="/assets?popup"
id="addAss" class="text-center cursor-pointer min-w-[150px] bg-green-600 px-4 py-2 text-white rounded hover:bg-green-800"
type="button">Add Asset</a>
<a href="/plans?openModal=true"
class="text-center cursor-pointer min-w-[150px] bg-green-600 px-4 py-2 text-white rounded hover:bg-green-800"
type="button">Create new plan </a>
</div>
<div class="w-full">
<div class="mb-12 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-y-4 gap-x-6">
<% if (plans.length === 0) { %>
<div class="col-span-1 col-start-2 ml-28 mt-25">
<div class="w-86 p-4 bg-white rounded-lg shadow-xl border border-slate-200">
<h1 class="text-2xl block text-center font-semibold text-slate-700">Don't have any retirement plans? </h1>
<div class="mt-4 flex justify-center items-center">
<div class="mt-4 flex justify-center">
<a href="/plans" class="cursor-pointer border-2 border-blue-600 bg-blue-600 text-white py-2 px-6
rounded-md hover:bg-blue-700 hover:border-blue-700 font-semibold
transition-colors duration-200 focus:outline-none ">Make one</a>
</div>
</div>
</div>
</div>
<% } else %>
<% plans.forEach(plan=> { %>
<%- include('./partials/dashboardBox.ejs', {thing: plan}) %>
<% }); %>
</div>
</div>
</main>
<script src="/static/scripts/dollarFormat.js"></script>
<%- include("./partials/navBar") %>
<%- include("./partials/scriptLoader") %>
<%- include("./partials/footer") %>
+62
View File
@@ -0,0 +1,62 @@
<%- include('./partials/fileHeader') %>
<%- include('./partials/header') %>
<main class="container mx-auto p-4 pt-28">
<div class="max-w-2xl mx-auto bg-gray-800 p-6 sm:p-8 rounded-xl shadow-xl">
<h2 class="text-2xl font-bold text-center text-white mb-8">Edit Plan: <%= plan.name %></h2>
<p class="text-sm text-gray-400 text-left mb-8"><span class="text-red-500">*</span> Indicates a required field</p>
<form id="editPlanForm" action="/plans/<%= plan._id %>/edit" method="POST" class="space-y-6">
<div>
<label for="name" class="block text-sm font-medium text-gray-300">Plan Name <span class="text-red-500">*</span></label>
<input type="text" name="name" id="name" value="<%= plan.name %>" required
class="mt-1 block w-full px-4 py-2 border border-gray-600 rounded-md shadow-sm bg-gray-700 text-white focus:ring-blue-500 focus:border-blue-500 sm:text-sm placeholder-gray-400">
</div>
<div>
<label for="retirementAge" class="block text-sm font-medium text-gray-300">Desired Retirement Age <span class="text-red-500">*</span></label>
<input type="number" name="retirementAge" id="retirementAge" value="<%= plan.retirementAge %>" required min="18"
class="mt-1 block w-full px-4 py-2 border border-gray-600 rounded-md shadow-sm bg-gray-700 text-white focus:ring-blue-500 focus:border-blue-500 sm:text-sm placeholder-gray-400">
</div>
<div>
<label for="retirementExpenses" class="block text-sm font-medium text-gray-300">Estimated Monthly Expenses at Retirement (<%= geoData.currency || 'CAD' %>) <span class="text-red-500">*</span></label>
<input type="number" name="retirementExpenses" id="retirementExpenses" value="<%= plan.retirementExpenses %>" required min="0" step="any"
class="mt-1 block w-full px-4 py-2 border border-gray-600 rounded-md shadow-sm bg-gray-700 text-white focus:ring-blue-500 focus:border-blue-500 sm:text-sm placeholder-gray-400">
</div>
<div>
<label for="retirementAssets" class="block text-sm font-medium text-gray-300">Target Retirement Assets (<%= geoData.currency || 'CAD' %>) <span class="text-red-500">*</span></label>
<input type="number" name="retirementAssets" id="retirementAssets" value="<%= plan.retirementAssets %>" required min="0" step="any"
class="mt-1 block w-full px-4 py-2 border border-gray-600 rounded-md shadow-sm bg-gray-700 text-white focus:ring-blue-500 focus:border-blue-500 sm:text-sm placeholder-gray-400">
</div>
<div>
<label for="retirementLiabilities" class="block text-sm font-medium text-gray-300">Estimated Monthly Liabilities at Retirement (<%= geoData.currency || 'CAD' %>) <span class="text-red-500">*</span></label>
<input type="number" name="retirementLiabilities" id="retirementLiabilities" value="<%= plan.retirementLiabilities %>" required min="0" step="any"
class="mt-1 block w-full px-4 py-2 border border-gray-600 rounded-md shadow-sm bg-gray-700 text-white focus:ring-blue-500 focus:border-blue-500 sm:text-sm placeholder-gray-400">
</div>
<div id="formErrorMessages" class="text-red-400 text-sm my-2" style="display: none;"></div>
<div class="flex items-center justify-center space-x-4 pt-4">
<button type="submit"
class="inline-flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-blue-500">
Save Changes
</button>
<button type="button" id="resetButton" class="inline-flex justify-center py-2 px-4 border border-gray-600 rounded-md shadow-sm bg-gray-600 text-sm font-medium text-white hover:bg-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-blue-500">
Reset
</button>
<a href="/plans/<%= plan._id %>" class="inline-flex justify-center py-2 px-4 border border-gray-600 rounded-md shadow-sm bg-gray-600 text-sm font-medium text-white hover:bg-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-blue-500">
Cancel
</a>
</div>
</form>
</div>
</main>
<script src="/static/scripts/editPlanModals.js"></script>
<%- include('./partials/navBar') %>
<%- include('./partials/scriptLoader') %>
<%- include('./partials/footer') %>
+42
View File
@@ -0,0 +1,42 @@
<%- include("./partials/fileHeader") %>
<%- include("./partials/headerStart") %>
<main class="pt-12">
<div class="flex justify-center items-center py-10">
<form action="/auth/resetPass" method="POST">
<div class="w-96 p-8 bg-white rounded-lg shadow-xl border border-slate-200">
<h1 class="text-3xl block text-center font-semibold text-slate-700">Reset Password</h1>
<hr class="mt-4 mb-6 border-slate-300">
<% if (!reset ) { %>
<div class="mt-4">
<label for="email" class="block text-base mb-2 text-slate-600">Email</label>
<input type="text" id="email" name="email"
class="border border-slate-300 focus:border-blue-500 w-full rounded-md text-base px-3 py-2 focus:outline-none focus:ring-1 focus:ring-blue-500 transition-colors duration-200"
placeholder="Enter Email" />
</div>
<% } else { %>
<div class="mt-4 text-center">
<h2 class="text-xl font-semibold text-green-600">Reset link sent.</h2>
<p class="text-sm text-slate-600 mt-2">Please check your email inbox (and spam folder).</p>
</div>
<% } %>
<% if (typeof error !== 'undefined' && error && error.length > 0) { %>
<div class="mt-3 text-red-600 font-semibold text-sm" id="forgor"><%= error %></div>
<% } else if (!reset) { %>
<div class="mt-3 h-5" id="forgor"></div> <% /* Placeholder for layout consistency */ %>
<% } %>
<% if (!reset) { %>
<div class="mt-6">
<button type="submit"
class="border-2 border-blue-600 bg-blue-600 text-white py-2 w-full rounded-md hover:bg-blue-700 hover:border-blue-700 font-semibold transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">Send Reset Link</button>
</div>
<% } %>
</div>
</form>
</div>
</main>
<%- include("./partials/footer") %>
-12
View File
@@ -1,12 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<h1>Welcome!</h1>
<form method="get" action="/signup">
<button type="submit">signup</button>
</form>
</body>
</html>
+6 -46
View File
@@ -1,46 +1,13 @@
<html> <%- include("./partials/fileHeader") %>
<head> <%- include("./partials/headerStart") %>
<title>RCalculator</title>
<link rel="icon" href="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/rekor.png">
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@3.3.2/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="bg-white dark:bg-gray-900">
<header>
<nav class="bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
<a href="#" class="flex items-center space-x-3 rtl:space-x-reverse">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/rekor.png" class="h-8" alt="Flowbite Logo" />
<span class="self-center text-2xl font-semibold whitespace-nowrap dark:text-white">RCalculator</span>
</a>
<button data-collapse-toggle="navbar-solid-bg" type="button" class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600" aria-controls="navbar-solid-bg" aria-expanded="false">
<span class="sr-only">Open main menu</span>
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15"/>
</svg>
</button>
<div class="hidden w-full md:block md:w-auto" id="navbar-solid-bg">
<ul class="flex flex-col font-medium mt-4 rounded-lg bg-gray-50 md:space-x-8 rtl:space-x-reverse md:flex-row md:mt-0 md:border-0 md:bg-transparent dark:bg-gray-800 md:dark:bg-transparent dark:border-gray-700">
<li>
<a href="/landing" class="block py-2 px-3 md:p-0 text-white bg-blue-700 rounded md:bg-transparent md:text-blue-700 md:dark:text-blue-500 dark:bg-blue-600 md:dark:bg-transparent" aria-current="page">Home</a>
</li>
<li>
<a href="/aboutUs" class="block py-2 px-3 md:p-0 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent">About Us</a>
</li>
<li>
<a href="mailto:rcalculator@gmail.com" class="block py-2 px-3 md:p-0 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent">Contact us</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<main> <main>
<section class="bg-center bg-no-repeat bg-cover bg-[url('/images/bg1.jpg')] bg-gray-400 bg-blend-multiply"> <section class="bg-center bg-no-repeat bg-cover bg-[url('/images/bg1.jpg')] bg-gray-400 bg-blend-multiply">
<div class="px-4 mx-auto max-w-screen-xl text-center py-24 lg:py-56"> <div class="px-4 mx-auto max-w-screen-xl text-center py-24 lg:py-56">
<h1 class="mb-4 text-4xl font-extrabold tracking-tight leading-none text-white md:text-5xl lg:text-6xl">Plan now, live better.</h1> <h1 class="mb-4 text-4xl font-extrabold tracking-tight leading-none text-white md:text-5xl lg:text-6xl">Plan now, live better.</h1>
<p class="mb-8 text-lg font-normal text-gray-300 lg:text-xl sm:px-16 lg:px-48">Planning your retirement is a crucial step towards financial security and a happy future.</p> <p class="mb-8 text-lg font-normal text-gray-300 lg:text-xl sm:px-16 lg:px-48">Planning your retirement is a crucial step towards financial security and a happy future.</p>
<div class="flex flex-col space-y-4 sm:flex-row sm:justify-center sm:space-y-0"> <div class="flex flex-col space-y-4 sm:flex-row sm:justify-center sm:space-y-0">
<a href="/login" class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 dark:focus:ring-blue-900"> <a href="/signup" class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-900">
Get started Get started
<svg class="w-3.5 h-3.5 ms-2 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 10"> <svg class="w-3.5 h-3.5 ms-2 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 10">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 5h12m0 0L9 1m4 4L9 9"/> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 5h12m0 0L9 1m4 4L9 9"/>
@@ -53,12 +20,5 @@
</div> </div>
</section> </section>
</main> </main>
<footer class="bg-gray-100 dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 p-6 text-center text-gray-500 dark:text-gray-400 text-sm">
<div class="container mx-auto"> <%- include("./partials/footer") %>
<p>&copy; 2025 RCalculator. All rights reserved.</p>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.3.0/flowbite.min.js"></script>
</body>
</html>
+50
View File
@@ -0,0 +1,50 @@
<%- include("./partials/fileHeader") %>
<%- include("./partials/headerStart") %>
<main class="pt-12">
<div class="flex justify-center items-center py-10">
<form action="/login" method="POST">
<div class="w-96 p-8 bg-white rounded-lg shadow-xl border border-slate-200">
<h1 class="text-3xl block text-center font-semibold text-slate-700">Login</h1>
<hr class="mt-4 mb-4 border-slate-300">
<p class="text-sm text-gray-400 text-left mb-4"><span class="text-red-500">*</span> Indicates a required field</p>
<div class="mt-4">
<label for="email" class="block text-base mb-2 text-slate-600">Email <span class="text-red-500">*</span></label>
<input required type="text" id="email" name="email"
class="border border-slate-300 focus:border-blue-500 w-full rounded-md text-base px-3 py-2 focus:outline-none focus:ring-1 focus:ring-blue-500 transition-colors duration-200"
placeholder="Enter Email" value="<%= typeof email != 'undefined' ? email : '' %>"/>
</div>
<div class="mt-4">
<label for="password" class="block text-base mb-2 text-slate-600">Password <span class="text-red-500">*</span></label>
<input required type="password" id="password" name="password"
class="border border-slate-300 focus:border-blue-500 w-full rounded-md text-base px-3 py-2 focus:outline-none focus:ring-1 focus:ring-blue-500 transition-colors duration-200"
placeholder="Enter Password" />
</div>
<% if (errMessage && errMessage.length > 0) { %>
<div class="mt-3 text-red-600 font-semibold text-sm" id="forgor"> <%= errMessage %></div>
<% } else { %>
<div class="mt-3 h-5" id="forgor"></div> <% /* Placeholder to maintain layout when no error */ %>
<% } %>
<div class="mt-4 flex justify-between items-center">
<div class="flex items-center">
<input type="checkbox" id="rememberMe" class="h-4 w-4 text-blue-600 border-slate-300 rounded focus:ring-blue-500">
<label for="rememberMe" class="ml-2 text-sm text-slate-600">Remember me</label>
</div>
<div>
<a href="/forgotPassword" class="text-sm text-blue-600 hover:text-blue-500 font-semibold transition-colors duration-200">Forgot Password? </a>
</div>
</div>
<div class="mt-6">
<button type="submit"
class="border-2 border-blue-600 bg-blue-600 text-white py-2 w-full rounded-md hover:bg-blue-700 hover:border-blue-700 font-semibold transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">Login</button>
</div>
<div class="mt-6 text-center">
<span class="text-sm text-slate-600">Don't have an account?</span>
<a href="/signup" class="text-sm text-blue-600 hover:text-blue-500 font-semibold ml-1 transition-colors duration-200">Register</a>
</div>
</div>
</form>
</div>
</main>
<%- include("./partials/footer") %>
+54
View File
@@ -0,0 +1,54 @@
<%- include("./partials/fileHeader") %>
<%- include("./partials/header") %>
<main class="pt-24 pb-10">
<div class="mt-6">
<div class="mb-12 grid gap-y-6 gap-x-6 md:grid-cols-2 mr-3 ml-3 xl:grid-cols-4">
<!--box to put logo icon if we want on in a box-->
<a href="/profile" class="relative flex flex-col bg-clip-border rounded-xl bg-white text-gray-700 shadow-md">
<div class="p-4 flex items-center justify-between">
<p class="font-sans text-2xl leading-normal font-normal text-blue-gray-600">
Profile
</p>
<svg class="w-[40px] h-[40px]" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="m10 16 4-4-4-4"/>
</svg>
</div>
</a>
<a href="/settings" class="relative flex flex-col bg-clip-border rounded-xl bg-white text-gray-700 shadow-md">
<div class="p-4 flex items-center justify-between">
<p class="font-sans text-2xl leading-normal font-normal text-blue-gray-600">
Settings
</p>
<svg class="w-[40px] h-[40px]" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="m10 16 4-4-4-4"/>
</svg>
</div>
</a>
<a href="/aboutUs" class="relative flex flex-col bg-clip-border rounded-xl bg-white text-gray-700 shadow-md">
<div class="p-4 flex items-center justify-between">
<p class="font-sans text-2xl leading-normal font-normal text-blue-gray-600">
About Us
</p>
<svg class="w-[40px] h-[40px]" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="m10 16 4-4-4-4"/>
</svg>
</div>
</a>
<a href="/logout" class="mt-77 relative flex flex-col bg-clip-border rounded-xl bg-white text-gray-700 shadow-md">
<div class="p-4 flex items-center justify-between">
<p class="font-sans text-2xl leading-normal font-normal text-blue-gray-600">
Log Out
</p>
<svg class="w-[40px] h-[40px]" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="m10 16 4-4-4-4"/>
</svg>
</div>
</a>
</div>
</div>
</main>
<%- include("./partials/navBar") %>
<%- include("./partials/scriptLoader") %>
<%- include("./partials/footer") %>
+24
View File
@@ -0,0 +1,24 @@
<%- include("./partials/fileHeader") %>
<%- include("./partials/headerStart") %>
<main>
<section class="bg-center bg-no-repeat bg-cover bg-[url('/images/bg1.jpg')] bg-gray-400 bg-blend-multiply">
<div class="px-4 mx-auto max-w-screen-xl text-center py-24 lg:py-56">
<h1 class="mb-4 text-4xl font-extrabold tracking-tight leading-none text-white md:text-5xl lg:text-6xl">404 - Not found</h1>
<p class="mb-8 text-lg font-normal text-gray-300 lg:text-xl sm:px-16 lg:px-48">It appears you stumbled accross a misleading page.</p>
<div class="flex flex-col space-y-4 sm:flex-row sm:justify-center sm:space-y-0">
<a href="/" class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-900">
Go home
<svg class="w-3.5 h-3.5 ms-2 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 10">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 5h12m0 0L9 1m4 4L9 9"/>
</svg>
</a>
<a href="/aboutUs" class="inline-flex justify-center hover:text-gray-900 items-center py-3 px-5 sm:ms-4 text-base font-medium text-center text-white rounded-lg border border-white hover:bg-gray-100 focus:ring-4 focus:ring-gray-400">
Learn more
</a>
</div>
</div>
</section>
</main>
<%- include("./partials/footer") %>
+42
View File
@@ -0,0 +1,42 @@
<!-- Confirm delete asset modal -->
<dialog id="<%= asset._id %>-delete-modal" class="w-lg mx-auto bg-transparent p-8 mt-10 rounded-lg shadow-md">
<div class="relative p-4 w-full max-w-md h-full md:h-auto">
<!-- Modal content -->
<div class="relative p-4 text-center rounded-lg shadow bg-gray-800 sm:p-5">
<form method="dialog">
<button
type="submit"
class="text-gray-500 absolute top-2.5 right-2.5 bg-transparent rounded-lg text-sm p-1.5 ml-auto inline-flex items-center hover:bg-gray-600 hover:text-white"
>
<svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
<span class="sr-only">Close modal</span>
</button>
</form>
<svg class="text-gray-500 w-11 h-11 mb-3.5 mx-auto" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd"></path></svg>
<p class="mb-4 text-gray-300">Are you sure you want to delete this item?</p>
<div class="flex justify-center items-center space-x-4">
<div style="width: 100%;">
<form method="dialog" style="width: 100%;">
<button
type="submit"
class="py-2 px-3 text-sm font-medium rounded-lg border focus:ring-4 focus:outline-none focus:ring-primary-300 focus:z-10 bg-gray-700 text-gray-300 border-gray-500 hover:text-white hover:bg-gray-600 focus:ring-gray-600"
>
No, cancel
</button>
</form>
</div>
<div style="width: 100%;">
<form method="POST" action="/deleteAsset">
<input id="id-delete-<%= asset._id %>" name="id" type="text" hidden value="<%= asset._id %>">
<button
type="submit"
class="py-2 px-3 text-sm font-medium text-center text-white rounded-lg focus:ring-4 focus:outline-none bg-red-500 hover:bg-red-600 focus:ring-red-900"
>
Yes, I'm sure
</button>
</form>
</div>
</div>
</div>
</div>
</dialog>
+209
View File
@@ -0,0 +1,209 @@
<!-- View/edit popup modal for asset -->
<dialog id="<%= asset._id %>-modal" class="w-lg mx-auto bg-white p-8 mt-10 rounded-lg shadow-md">
<div class="flex flex-row justify-between">
<div style="width: 45%;">
<form method="dialog" style="width: 100%;">
<button
style="width: 100%;"
onclick="lockAsset('<%= asset._id %>', '<%= asset.icon %>')"
type="submit"
class="text-center w-sm py-2 py-2 mx-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white
bg-blue-600 hover:bg-blue-700 focus:outline-none "
>
Close
</button>
</form>
</div>
<div style="width: 45%;">
<button
style="width: 100%;"
type="submit"
class="text-center w-sm py-2 py-2 mx-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 cursor-pointer"
onclick="document.getElementById('<%= asset._id %>-delete-modal').showModal()"
>
Delete
</button>
</div>
</div>
<br>
<div class="flex flex-row justify-between">
<% if (asset.type == "other") { %>
<div class="flex items-center">
<button
id="dropdown-icon-button-<%= asset._id %>"
value=""
data-dropdown-toggle="dropdown-icons-<%= asset._id %>"
class="shrink-0 z-10 inline-flex items-center py-2.5 px-4 text-sm font-medium text-center border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none"
type="button"
>
<img src="/static/svgs/icons/<%= asset.icon %>.svg" class="h-4 w-4 me-2" alt="<%= asset.icon %>"> <%= asset.icon %>
<svg class="w-2.5 h-2.5 ms-2.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
</svg>
</button>
<div id="dropdown-icons-<%= asset._id %>" class="overflow-y-auto h-40 z-10 hidden divide-y divide-gray-100 rounded-lg shadow-sm w-32 bg-gray-700">
<ul id="dropdown-<%= asset._id %>" class="py-2 text-sm text-gray-200" aria-labelledby="dropdown-icon-button-<%= asset._id %>">
<% icons.forEach(icon => { %>
<li>
<button onclick="selectIcon(this, '<%= asset._id %>')" type="button" value="<%= icon %>" class="inline-flex w-full px-4 py-2 text-sm text-gray-200 hover:bg-gray-600 hover:text-white" role="menuitem">
<span class="inline-flex items-center">
<img src="/static/svgs/icons/<%= icon %>.svg" class="h-4 w-4 me-2" alt="<%= icon %>"> (<%= icon %>)
</span>
</button>
</li>
<% }); %>
</ul>
</div>
</div>
<% } else { %>
<button
disabled
class="inline-flex items-center py-2.5 px-10 text-sm font-medium text-center border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none"
type="button"
>
<img src="/static/svgs/icons/<%= asset.icon %>.svg" class="h-4 w-4" alt="<%= asset.icon %>">
</button>
<% } %>
<h2 class="text-xl font-medium tracking-tight leading-none mt-3"><%= String(asset.type).charAt(0).toUpperCase() + String(asset.type).slice(1); %> Asset</h2>
<div>
<button
id="edit-<%= asset._id %>"
onclick="unlockAsset('<%= asset._id %>')"
class="py-3 px-8 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 cursor-pointer"
>
Edit
</button>
</div>
</div>
<br>
<form id="<%= asset._id %>-form" method="POST" action="/updateAsset">
<input id="type-<%= asset._id %>" name="type" type="text" hidden value="<%= asset.type %>">
<input id="id-<%= asset._id %>" name="id" type="text" hidden value="<%= asset._id %>">
<input id="userId-<%= asset.userId %>" name="userId" type="text" hidden value="<%= asset.userId %>">
<input id="icon-<%= asset._id %>" name="icon" type="text" hidden value="<%= asset.icon %>">
<% if (asset.type != "stock") { %>
<label for="name" class="block text-sm font-medium text-gray-700 mt-6">Name <span class="text-red-500">*</span></label>
<input
id="name-<%= asset._id %>"
disabled
type="text"
name="name"
value="<%= asset.name %>"
class="mt-1 mb-2 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none"
>
<% } else { %>
<label for="ticker" class="block text-sm font-medium text-gray-700 mt-2">Stock Ticker <span class="text-red-500">*</span></label>
<input
id="ticker-<%= asset._id %>"
disabled
type="text"
name="ticker"
value="<%= asset.ticker %>"
class="mt-1 mb-2 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none"
>
<% } %>
<hr class="h-px mt-6 mb-4 bg-gray-500 border-0 mx-10">
<% if (asset.type != "stock") { %>
<label for="value" class="block text-sm font-medium text-gray-700">Value <span class="text-red-500">*</span></label>
<input
id="value-<%= asset._id %>"
disabled
type="number"
step=".01"
name="value"
value="<%= asset.value %>"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none"
>
<div
id="year-modify-<%= asset._id %>"
<% if (asset.icon != "Car" && asset.icon != "Motorcycle") { %>
style="display: none;"
<% } %>
>
<label for="year" class="block text-sm font-medium text-gray-700 mt-2">Year <span class="text-red-500">*</span></label>
<input
id="year-<%= asset._id %>"
disabled
required
placeholder="2000"
min="1900"
max="<% new Date().getFullYear() %>"
type="number"
name="year"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none"
value="<%= asset.year %>"
>
</div>
<% } else { %>
<label for="price" class="block text-sm font-medium text-gray-700 mt-2">Price per Share <span class="text-red-500">*</span></label>
<input
id="price-<%= asset._id %>"
disabled
type="number"
step=".01"
name="price"
value="<%= asset.price %>"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none"
>
<label for="quantity" class="block text-sm font-medium text-gray-700 mt-2">Quantity of Shares <span class="text-red-500">*</span></label>
<input
id="quantity-<%= asset._id %>"
disabled
type="number"
name="quantity"
value="<%= asset.quantity %>"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none"
>
<% } %>
<% if (asset.type == "other") { %>
<label for="description" class="block text-sm font-medium text-gray-700 mt-2">Description</label>
<textarea
disabled
maxlength="240"
id="description-<%= asset._id %>"
name="description"
rows="4"
cols="50"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none"
><%= asset.description %></textarea>
<% } %>
<% if (asset.type != "saving") { %>
<label for="purchaseDate" class="block text-sm font-medium text-gray-700 mt-2">Purchased on <span class="text-red-500">*</span></label>
<input
id="purchaseDate-<%= asset._id %>"
disabled
type="date"
name="purchaseDate"
value="<%= asset.purchaseDate %>"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none"
>
<% } %>
<br>
<% let options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }; %>
<small class="block text-sm font-medium text-gray-700">Last Modified: <%= asset.updatedAt.toLocaleDateString("en-US", options); %></small>
<br>
<div>
<button
id="save-<%= asset._id %>"
disabled
type="submit"
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 cursor-not-allowed"
>
Save
</button>
</div>
</form>
</dialog>
+22
View File
@@ -0,0 +1,22 @@
<!-- Clickable asset list item -->
<div class="mt-4 max-w-md mx-auto bg-white rounded-lg shadow-md">
<button
onclick="document.getElementById('<%= asset._id %>-modal').showModal()"
style="width: 100%; height: 100%;"
class="px-4 py-4 cursor-pointer"
>
<div class="flex flex-row justify-between">
<div class="flex flex-row justify-between">
<div class="mx-2">
<img src="/static/svgs/icons/<%= asset.icon %>.svg" class="h-6 w-6 me-2" alt="<%= asset.icon %>">
</div>
<div class="mx-2"><h3><%= asset.name %></h3></div>
</div>
<div>
<h3><strong>
<%- new Intl.NumberFormat('en-US', { style: 'currency', currency: geoData.currency ? geoData.currency : 'CAD' }).format(asset.value) %>
</strong></h3>
</div>
</div>
</button>
</div>
+276
View File
@@ -0,0 +1,276 @@
<!-- Create Asset popup Modal -->
<dialog id="create-asset-modal" class="w-lg mx-auto bg-white p-8 mt-10 rounded-lg shadow-md">
<div class="flex flex-row justify-between">
<h2 class="text-lg font-bold mb-2">Create New Asset</h2>
<form method="dialog">
<button
onclick="resetAll()"
type="submit"
class="text-center py-2 mx-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none cursor-pointer"
>
Cancel
</button>
</form>
</div>
<p class="text-sm text-gray-400 text-left mb-4"><span class="text-red-500">*</span> Indicates a required field</p>
<div class="my-4">
<label class="block text-sm font-medium text-gray-700">Asset Type</label>
<div class="mt-1 space-x-4">
<label class="inline-flex items-center">
<input
checked
onclick="selectAssetForm('create-other-asset-form')"
id="asset-other"
type="radio"
name="type"
value="other"
class="form-radio h-4 w-4 text-blue-600 border-gray-300 focus:ring-blue-500 cursor-pointer"
>
<span class="ml-2 text-sm text-gray-700">Other Assets</span>
</label>
<label class="inline-flex items-center">
<input
onclick="selectAssetForm('create-saving-asset-form')"
id="asset-saving"
type="radio"
name="type"
value="saving"
class="form-radio h-4 w-4 text-blue-600 border-gray-300 focus:ring-blue-500 cursor-pointer"
>
<span class="ml-2 text-sm text-gray-700">Savings</span>
</label>
<label class="inline-flex items-center">
<input
onclick="selectAssetForm('create-stock-asset-form')"
id="asset-stock"
type="radio"
name="type"
value="stock"
class="form-radio h-4 w-4 text-blue-600 border-gray-300 focus:ring-blue-500 cursor-pointer"
>
<span class="ml-2 text-sm text-gray-700">Stocks</span>
</label>
</div>
</div>
<!-- Create other asset form -->
<form method="POST" action="/createAsset" id="create-other-asset-form">
<input style="display: none;" id="type" value="other" name="type" type="text">
<input style="display: none;" id="icon" value="Other" name="icon" type="text">
<label for="name" class="block text-sm font-medium text-gray-700 mt-2">Select an Icon</label>
<div class="flex items-center">
<button
id="dropdown-icon-button"
data-dropdown-toggle="dropdown-icons"
class="shrink-0 z-10 inline-flex items-center py-2.5 px-4 text-sm font-medium text-center border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
type="button"
>
<img src="/static/svgs/icons/Other.svg" class="h-4 w-4 me-2" alt="Other"> Other
<svg class="w-2.5 h-2.5 ms-2.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
</svg>
</button>
<div id="dropdown-icons" class="overflow-y-auto h-40 z-10 hidden divide-y divide-gray-100 rounded-lg shadow-sm w-32 bg-gray-700">
<ul id="dropdown" class="py-2 text-sm text-gray-200" aria-labelledby="dropdown-icon-button">
<% icons.forEach(icon => { %>
<li>
<button
onclick="selectIcon(this)"
type="button"
value="<%= icon %>"
class="inline-flex w-full px-4 py-2 text-sm text-gray-200 hover:bg-gray-600 hover:text-white"
role="menuitem"
>
<span class="inline-flex items-center">
<img src="/static/svgs/icons/<%= icon %>.svg" class="h-4 w-4 me-2" alt="<%= icon %>"> (<%= icon %>)
</span>
</button>
</li>
<% }); %>
</ul>
</div>
</div>
<label for="name" class="block text-sm font-medium text-gray-700 mt-2">Asset Name <span class="text-red-500">*</span></label>
<input
required
id="name"
type="text"
name="name"
minlength="3"
maxlength="30"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
>
<div style="display: none;" id="year-create">
<label for="year" class="block text-sm font-medium text-gray-700 mt-2">Year <span class="text-red-500">*</span></label>
<input
id="year-input"
disabled
required
placeholder="2000"
min="1900"
max="<% new Date().getFullYear() %>"
type="number"
name="year"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
>
</div>
<label for="value" class="block text-sm font-medium text-gray-700 mt-2">Asset Value <span class="text-red-500">*</span></label>
<input
required
placeholder="CAD"
min="0"
step=".01"
id="value"
type="number"
name="value"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
>
<label for="purchaseDate" class="block text-sm font-medium text-gray-700 mt-2">Date of Purchase <span class="text-red-500">*</span></label>
<input
required
type="date"
id="purchaseDate"
name="purchaseDate"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
>
<label for="description" class="block text-sm font-medium text-gray-700 mt-2">Description</label>
<textarea
maxlength="240"
id="description"
name="description"
rows="4"
cols="50"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
></textarea>
<div class="mt-6 flex flex-row justify-between">
<button
type="reset"
onclick="document.getElementById('create-other-asset-form').reset()"
class="text-center w-sm py-2 mx-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 cursor-pointer"
>
Clear
</button>
<button
type="submit"
class="text-center w-sm py-2 mx-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 cursor-pointer"
>
Create
</button>
</div>
</form>
<!-- Create savings asset form -->
<form style="display: none;" method="POST" action="/createAsset" id="create-saving-asset-form">
<input style="display: none;" value="saving" name="type" type="text">
<label for="name" class="block text-sm font-medium text-gray-700 mt-2">Savings Name <span class="text-red-500">*</span></label>
<input
required
id="name"
type="text"
name="name"
minlength="3"
maxlength="30"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
>
<label for="value" class="block text-sm font-medium text-gray-700 mt-2">Savings Value <span class="text-red-500">*</span></label>
<input
required
placeholder="CAD"
min="0"
step=".01"
id="value"
type="number"
name="value"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
>
<div class="mt-6 flex flex-row justify-between">
<button
type="reset"
onclick="document.getElementById('create-saving-asset-form').reset()"
class="text-center w-sm py-2 mx-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 cursor-pointer"
>
Clear
</button>
<button
type="submit"
class="text-center w-sm py-2 mx-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 cursor-pointer"
>
Create
</button>
</div>
</form>
<!-- Create stock asset form -->
<form style="display: none;" method="POST" action="/createAsset" id="create-stock-asset-form">
<input style="display: none;" value="stock" name="type" type="text">
<label for="ticker" class="block text-sm font-medium text-gray-700 mt-2">Stock Ticker <span class="text-red-500">*</span></label>
<input
required
id="ticker"
type="text"
name="ticker"
minlength="3"
maxlength="5"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
>
<label for="price" class="block text-sm font-medium text-gray-700 mt-2">Price per Share <span class="text-red-500">*</span></label>
<input
required
placeholder="CAD"
min="0"
step=".01"
id="price"
type="number"
name="price"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
>
<label for="quantity" class="block text-sm font-medium text-gray-700 mt-2">Quantity of Shares <span class="text-red-500">*</span></label>
<input
required
type="number"
min="1"
id="quantity"
name="quantity"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
>
<label for="purchaseDate" class="block text-sm font-medium text-gray-700 mt-2">Date of Purchase <span class="text-red-500">*</span></label>
<input
required
type="date"
id="purchaseDate"
name="purchaseDate"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
>
<div class="mt-6 flex flex-row justify-between">
<button
type="reset"
onclick="document.getElementById('create-stock-asset-form').reset()"
class="text-center w-sm py-2 mx-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 cursor-pointer"
>
Clear
</button>
<button
type="submit"
class="text-center w-sm py-2 mx-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 cursor-pointer"
>
Create
</button>
</div>
</form>
</dialog>
+35
View File
@@ -0,0 +1,35 @@
<div class="w-full h-full mt-3">
<a href="/plans/<%=thing._id%>"
class="block p-4 w-full h-full rounded-xl transition-shadow duration-300 ease-in-out hover:shadow-xl">
<div
class="relative flex flex-col bg-white text-gray-700 shadow-md rounded-xl w-full h-full overflow-hidden min-h-[175px]">
<!-- Header Section -->
<div class="p-4 flex items-center justify-between border-b ">
<p class="text-2xl font-bold tracking-tight text-blue-gray-700 truncate" title="<%= thing.name %>">
<%= thing.name %>
</p>
<h4 class="planGoal text-2xl font-bold tracking-tight whitespace-nowrap">
<%= thing.retirementAssets %>
</h4>
</div>
<!-- Progress Bar Section - Pushed to the bottom -->
<div class="mt-2">
<div class="p-4 mt-auto border-gray-900">
<div class="flex justify-between items-center mb-1">
<p class="text-lg font-bold tracking-tight text-blue-gray-600">
Progress:
</p>
<p class="text-sm font-semibold justify-between text-green-500">
<%=Math.floor(thing.progress)> 100 ? 100 : Math.floor(thing.progress) %>%
</p>
</div>
<div class="w-full bg-gray-300 rounded-full h-2.5">
<div class="bg-green-500 h-2.5 rounded-full"
style="width: <%= thing.progress > 100 ? 100 : thing.progress %>%;"></div>
</div>
</div>
</div>
</div>
</a>
</div>
+21
View File
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<title>RCalculator</title>
<style>
::-webkit-scrollbar {
display: none;
}
* {
scrollbar-width: none;
-ms-overflow-style: none;
}
</style>
</head>
<body class="bg-gray-900 overflow-y-auto">
+28
View File
@@ -0,0 +1,28 @@
<div class="fixed end-6 bottom-20 group">
<div id="factMenu" class="absolute right-0 bottom-full mb-2 hidden">
<div id="factContainer" class="p-4 rounded-lg shadow-md bg-gray-700 w-64">
<p class="mb-2 text-sm text-gray-300">Insert the kind of short fact you would like to know more about:</p>
<input type="text" id="factInput" placeholder="Enter your topic here" class="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 bg-gray-600 border-gray-500 text-white placeholder-gray-400">
<button type="button" id="factSubmitButton" class="mt-3 w-full px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-gray-700">Submit</button>
</div>
</div>
<button type="button" id="factButton" class="flex items-center justify-center text-white rounded-full w-14 h-14 bg-blue-600 hover:bg-blue-700 focus:ring-4 focus:outline-none focus:ring-blue-800 hidden">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path></svg>
<span class="sr-only">Get a Fact</span>
</button>
</div>
<footer class="mt-16">
<div class="fixed bottom-0 left-0 z-30 hidden lg:block w-full h-16 bg-gray-800 border-t border-gray-700 p-6 text-center text-gray-400 text-sm">
<div class="container mx-auto">
<p>&copy; 2025 RCalculator. All rights reserved.</p>
</div>
</div>
</footer>
<script src="/static/scripts/footer.js"></script>
<script src="https://cdn.jsdelivr.net/npm/flowbite@3.1.2/dist/flowbite.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
</body>
</html>
+140
View File
@@ -0,0 +1,140 @@
<header>
<nav class="fixed bg-gray-800 border-b border-gray-700 z-30">
<div class="w-screen flex flex-col md:flex-row flex-wrap items-center justify-between p-3">
<a href="/home" class="flex xl:w-[320px] items-center space-x-3 rtl:space-x-reverse">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/rekor.png" class="h-8" alt="Flowbite Logo" />
<span class="self-center text-2xl font-semibold whitespace-nowrap text-white">RCalculator</span>
</a>
<% if (!geoData.country) { %>
<div role="status" id="loading">
<svg aria-hidden="true" class="w-8 h-8 animate-spin text-gray-600 fill-blue-600" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/>
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/>
</svg>
<span class="sr-only">Loading...</span>
</div>
<div style="display: none;" class="flex items-center pt-2 md:pt-0 lg:pt-0" id="currencyExchange">
<div id="exFrom" class="shrink-0 z-10 inline-flex w-[115px] items-center py-2.5 px-4 text-sm font-medium text-center border rounded-s-lg focus:ring-4 focus:outline-none bg-gray-700 focus:ring-gray-700 text-white border-gray-600">
<img src="" id="yourFlag" class="h-4 w-4 me-2" alt="">
<p id="flagTag"></p>
</div>
<div class="relative w-full">
<p id="exchange" class="block text-center p-2.5 w-35 z-20 text-sm border bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:border-blue-500">
</p>
</div>
<button id="dropdown-country-button" value="" data-dropdown-toggle="dropdown-country" class="shrink-0 z-10 inline-flex items-center py-2.5 px-4 text-sm font-medium text-center border rounded-e-lg focus:ring-4 focus:outline-none bg-gray-700 hover:bg-gray-600 focus:ring-gray-700 text-white border-gray-600" type="button">
<img src="" id="exchangeFlag" class="h-4 w-4 me-2" alt="">
<p id="exFlagTag"></p>
<svg class="w-2.5 h-2.5 ms-2.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
</svg>
</button>
<div id="dropdown-country" class="overflow-y-auto w-[115px] h-40 z-10 hidden divide-y divide-gray-100 rounded-lg shadow-sm w-32 bg-gray-700">
<ul id="dropdown" class="py-2 text-sm text-gray-200" aria-labelledby="dropdown-country-button">
</ul>
</div>
</div>
<% } else if(geoData.message == "error") { %>
<p>error</p>
<% } else { %>
<div class="flex items-center pt-2 md:pt-0 lg:pt-0">
<div class="shrink-0 z-10 w-[115px] inline-flex items-center py-2.5 px-4 text-sm font-medium text-center border rounded-s-lg focus:ring-4 focus:outline-none bg-gray-700 focus:ring-gray-700 text-white border-gray-600">
<img src="/static/svgs/flags/<%= geoData.country %>.svg" id="yourFlag" class="h-4 w-4 me-2" alt="<%= geoData.country %>"> (<%= geoData.country %>)
<p id="flagTag"></p>
</div>
<div class="relative w-full">
<p id="exchange" class="block text-center p-2.5 w-35 z-20 text-sm border-s-0 border bg-gray-700 border-s-gray-700 border-gray-600 placeholder-gray-400 text-white focus:border-blue-500">
$1.00 = $<%= (1 * geoData.toCurrencyRates["USD"]).toFixed(2) %>
</p>
</div>
<button id="dropdown-country-button" value="" data-dropdown-toggle="dropdown-country" class="shrink-0 w-[115px] z-10 inline-flex items-center py-2.5 px-4 text-sm font-medium text-center border rounded-e-lg focus:ring-4 focus:outline-none bg-gray-700 hover:bg-gray-600 focus:ring-gray-700 text-white border-gray-600" type="button">
<img src="/static/svgs/flags/USD.svg" class="h-4 w-4 me-2" alt="USD"> (USD)
<svg class="w-2.5 h-2.5 ms-2.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
</svg>
</button>
<div id="dropdown-country" class="overflow-y-auto h-40 z-10 hidden divide-y divide-gray-100 rounded-lg shadow-sm w-32 bg-gray-700">
<ul id="dropdown" class="py-2 text-sm text-gray-200" aria-labelledby="dropdown-country-button">
<% let countries = Object.keys(geoData.toCurrencyRates);%>
<% countries.forEach(item => { %>
<li>
<button onclick="switchButton(this)" type="button" value="<%= geoData.toCurrencyRates[item] %>" class="countryButton inline-flex w-full px-4 py-2 text-sm text-gray-200 hover:bg-gray-600 hover:text-white" role="menuitem">
<span class="inline-flex items-center">
<img src="/static/svgs/flags/<%= item %>.svg" class="h-4 w-4 me-2" alt="<%= item %>"> (<%= item %>)
</span>
</button>
</li>
<% }); %>
</ul>
</div>
</div>
<% } %>
<div class="hidden w-full lg:block lg:w-[320px]" id="navbar-solid-bg">
<ul class="flex flex-col font-medium mt-4 rounded-lg lg:space-x-8 rtl:space-x-reverse lg:flex-row lg:mt-0 lg:border-0 bg-gray-800 lg:bg-transparent border-gray-700">
<li class="flex items-center justify-center">
<a href="/home" class="block w-full text-center py-2 px-3 lg:p-0 rounded lg:border-0 text-white lg:hover:text-blue-500 hover:bg-gray-700 hover:text-white lg:hover:bg-transparent">Dashboard</a>
</li>
<li class="flex items-center justify-center">
<a href="/assets" class="block w-full text-center py-2 px-3 lg:p-0 rounded lg:border-0 text-white lg:hover:text-blue-500 hover:bg-gray-700 hover:text-white lg:hover:bg-transparent">Assets</a>
</li>
<li class="flex items-center justify-center">
<a href="/plans" class="block w-full text-center py-2 px-3 lg:p-0 rounded lg:border-0 text-white lg:hover:text-blue-500 hover:bg-gray-700 hover:text-white lg:hover:bg-transparent">Plans</a>
</li>
<li>
<div class="text-center">
<button class="flex flex-row text-white hover:bg-blue-700 focus:ring-4 font-medium rounded-full text-sm focus:outline-none focus:ring-blue-800" type="button" id="drawer-toggle" data-drawer-target="drawer-navigation" data-drawer-placement="right" aria-controls="drawer-navigation">
<svg class="w-[30px] h-[30px] text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M12 20a7.966 7.966 0 0 1-5.002-1.756l.002.001v-.683c0-1.794 1.492-3.25 3.333-3.25h3.334c1.84 0 3.333 1.456 3.333 3.25v.683A7.966 7.966 0 0 1 12 20ZM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10c0 5.5-4.44 9.963-9.932 10h-.138C6.438 21.962 2 17.5 2 12Zm10-5c-1.84 0-3.333 1.455-3.333 3.25S10.159 13.5 12 13.5c1.84 0 3.333-1.455 3.333-3.25S13.841 7 12 7Z" clip-rule="evenodd"/>
</svg>
<svg class="w-[30px] h-[30px] text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M5 7h14M5 12h14M5 17h14"/>
</svg>
</button>
</div>
</li>
</ul>
</div>
<!-- drawer component -->
</div>
</nav>
<div id="drawer-navigation" class="lg:block fixed top-0 right-0 z-60 w-64 h-screen p-4 overflow-y-auto transition-transform translate-x-full bg-gray-800" tabindex="-1" aria-labelledby="drawer-navigation-label">
<h5 id="drawer-navigation-label" class="text-base font-semibold uppercase text-gray-400"><%= user.name %></h5>
<button type="button" data-drawer-hide="drawer-navigation" aria-controls="drawer-navigation" class="text-gray-400 bg-transparent rounded-lg text-sm p-1.5 absolute top-2.5 end-2.5 inline-flex items-center hover:bg-gray-600 hover:text-white" >
<svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
<span class="sr-only">Close menu</span>
</button>
<div class="">
<div class="w-full pt-5 pl-2.5">
<a href="/logout" class="text-white focus:outline-none focus:ring-4 font-medium rounded-full text-sm px-20 py-2.5 text-center me-2 mb-2 bg-blue-600 hover:bg-blue-700 focus:ring-blue-800">
Logout
</a>
</div>
</div>
<div class="py-4 overflow-y-auto">
<ul class="space-y-2 font-medium">
<li>
<a href="/profile" class="flex items-center p-2 rounded-lg text-white hover:bg-gray-700 group">
<svg class="w-5 h-5 text-gray-500 text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 22 21">
<path fill-rule="evenodd" d="M12 20a7.966 7.966 0 0 1-5.002-1.756l.002.001v-.683c0-1.794 1.492-3.25 3.333-3.25h3.334c1.84 0 3.333 1.456 3.333 3.25v.683A7.966 7.966 0 0 1 12 20ZM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10c0 5.5-4.44 9.963-9.932 10h-.138C6.438 21.962 2 17.5 2 12Zm10-5c-1.84 0-3.333 1.455-3.333 3.25S10.159 13.5 12 13.5c1.84 0 3.333-1.455 3.333-3.25S13.841 7 12 7Z" clip-rule="evenodd"/>
</svg>
<span class="ms-3">Profile</span>
</a>
</li>
<li>
<a href="aboutUs" class="flex items-center p-2 rounded-lg text-white hover:bg-gray-700 group">
<svg class="shrink-0 w-5 h-5 transition duration-75 text-gray-400 group-hover:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18">
<path d="M14 2a3.963 3.963 0 0 0-1.4.267 6.439 6.439 0 0 1-1.331 6.638A4 4 0 1 0 14 2Zm1 9h-1.264A6.957 6.957 0 0 1 15 15v2a2.97 2.97 0 0 1-.184 1H19a1 1 0 0 0 1-1v-1a5.006 5.006 0 0 0-5-5ZM6.5 9a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9ZM8 10H5a5.006 5.006 0 0 0-5 5v2a1 1 0 0 0 1 1h11a1 1 0 0 0 1-1v-2a5.006 5.006 0 0 0-5-5Z"/>
</svg>
<span class="flex-1 ms-3 whitespace-nowrap">About Us</span>
</a>
</li>
</ul>
</div>
</div>
</header>
+32
View File
@@ -0,0 +1,32 @@
<header>
<nav class="bg-gray-800 border-b border-gray-700">
<div class="w-screen flex flex-wrap items-center justify-between p-4">
<a href="/" class="flex items-center space-x-3 rtl:space-x-reverse">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/rekor.png" class="h-8" alt="Flowbite Logo" />
<span class="self-center text-2xl font-semibold whitespace-nowrap text-white">RCalculator</span>
</a>
<button data-collapse-toggle="navbar-solid-bg" type="button"
class="inline-flex items-center justify-center text-sm rounded-lg lg:hidden focus:outline-none focus:ring-2 text-gray-400 hover:bg-gray-700 focus:ring-gray-600"
aria-controls="navbar-solid-bg" aria-expanded="false">
<span class="sr-only">Open main menu</span>
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="none" viewBox="0 0 17 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M1 1h15M1 7h15M1 13h15" />
</svg>
</button>
<div class="hidden w-full lg:block lg:w-auto" id="navbar-solid-bg">
<ul class="flex flex-col font-medium mt-4 rounded-lg lg:space-x-8 rtl:space-x-reverse lg:flex-row lg:mt-0 lg:border-0 bg-gray-800 lg:bg-transparent border-gray-700">
<li>
<a href="/" class="block py-2 px-3 lg:p-0 rounded lg:border-0 text-white lg:hover:text-blue-500 hover:bg-gray-700 hover:text-white lg:hover:bg-transparent">Home</a>
</li>
<li>
<a href="/login" class="block py-2 px-3 lg:p-0 rounded lg:border-0 text-white lg:hover:text-blue-500 hover:bg-gray-700 hover:text-white lg:hover:bg-transparent">Login</a>
</li>
<li>
<a href="mailto:rcalculator@gmail.com" class="block py-2 px-3 lg:p-0 rounded lg:border-0 text-white lg:hover:text-blue-500 hover:bg-gray-700 hover:text-white lg:hover:bg-transparent">Contact us</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
+20
View File
@@ -0,0 +1,20 @@
<div class="fixed block bottom-0 left-0 z-50 lg:hidden w-full h-16 bg-gray-800 border-t border-gray-700 text-gray-400 text-sm">
<div class="grid h-16 grid-cols-4 mx-auto font-medium">
<a href="/home" class="inline-flex flex-col items-center justify-center px-5 hover:bg-gray-800 group">
<img src="/static/svgs/dashboard.svg" alt="dashboard">
<span class="text-gray-400 text-sm">Dashboard</span>
</a>
<a href="/assets" class="inline-flex flex-col items-center justify-center px-5 hover:bg-gray-800 group">
<img src="/static/svgs/assets.svg" alt="assets">
<span class="text-gray-400 text-sm">Assets</span>
</a>
<a href="/plans" class="inline-flex flex-col items-center justify-center px-5 hover:bg-gray-800 group">
<img src="/static/svgs/plans.svg" alt="plans">
<span class="text-gray-400 text-sm">Plans</span>
</a>
<a href="/more" class="inline-flex flex-col items-center justify-center px-5 hover:bg-gray-800 group">
<img src="/static/svgs/more.svg" alt="more">
<span class="text-gray-400 text-sm">More</span>
</a>
</div>
</div>
+7
View File
@@ -0,0 +1,7 @@
<!-- Get geolocation if not in session and load conversion rates -->
<script src="/static/scripts/geolocation.js"></script>
<script>
// If no geoData provided thorugh EJS, send update request to backend
let country = "<%= geoData.country %>";
if (!country) getLocation();
</script>
+102
View File
@@ -0,0 +1,102 @@
<%- include("./partials/fileHeader") %>
<%- include("./partials/header") %>
<main class="container mx-auto p-4 pt-28">
<div class="max-w-lg mx-auto p-6 sm:p-8 rounded-xl shadow-lg space-y-6 bg-gray-800">
<div class="text-center">
<h3 class="text-2xl font-bold text-white"><%= plan.name %></h3>
</div>
<div>
<div class="flex justify-between mb-1">
<span class="text-sm font-medium text-blue-400">Progress</span>
<span class="text-sm font-medium text-blue-400"><%-plan.progress > 100 ? 100 : plan.progress%>%</span>
</div>
<div class="w-full rounded-full h-3 bg-gray-700">
<div class="h-3 rounded-full bg-blue-500" style="width: <%= plan.progress > 100 ? 100 : plan.progress %>%;"></div>
</div>
</div>
<div>
<div class="flex justify-between mb-1">
<span class="text-sm font-medium text-blue-400">Calculated Monthly Investment</span>
<span class="text-sm font-medium text-blue-400"><%- new Intl.NumberFormat('en-US', { style: 'currency', currency: geoData.currency ? geoData.currency : 'CAD' }).format(progress.monthlyInvestment > 0 ? progress.monthlyInvestment : 0) %></span>
</div>
</div>
<div class="border-t border-gray-700 pt-6 space-y-4">
<h4 class="text-lg font-semibold text-gray-300 mb-3">Assets:</h4>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-4">
<div>
<label class="block text-sm font-medium text-gray-400">Number of Assets:</label>
<p class="mt-1 text-md text-white"><%= assets.length %></p>
</div>
<div>
<label class="block text-sm font-medium text-gray-400">Total Value:</label>
<p class="mt-1 text-md text-white"><%- new Intl.NumberFormat('en-US', { style: 'currency', currency: geoData.currency ? geoData.currency : 'CAD' }).format(totalUserAssetValue) %></p>
</div>
</div>
</div>
<div class="border-t border-gray-700 pt-6 space-y-4">
<h4 class="text-lg font-semibold text-gray-300 mb-3">Plan Details:</h4>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-4">
<div>
<label class="block text-sm font-medium text-gray-400">Retirement Age:</label>
<p class="mt-1 text-md text-white"><%= plan.retirementAge %></p>
</div>
<div>
<label class="block text-sm font-medium text-gray-400">Target Monthly Expenses (Minus Liabilities):</label>
<p class="mt-1 text-md text-white"><%- new Intl.NumberFormat('en-US', { style: 'currency', currency: geoData.currency ? geoData.currency : 'CAD' }).format(plan.retirementExpenses) %></p>
</div>
<div>
<label class="block text-sm font-medium text-gray-400">Target Retirement Assets (Total):</label>
<p class="mt-1 text-md text-white"><%- new Intl.NumberFormat('en-US', { style: 'currency', currency: geoData.currency ? geoData.currency : 'CAD' }).format(plan.retirementAssets) %></p>
</div>
<div>
<label class="block text-sm font-medium text-gray-400">Target Retirement Liabilities (Monthly):</label>
<p class="mt-1 text-md text-white"><%- new Intl.NumberFormat('en-US', { style: 'currency', currency: geoData.currency ? geoData.currency : 'CAD' }).format(plan.retirementLiabilities) %></p>
</div>
<div>
<label class="block text-sm font-medium text-gray-400">Years Until Retirement:</label>
<p class="mt-1 text-md text-white"><%- progress.yearsUntilRetirement %></p>
</div>
<div>
<label class="block text-sm font-medium text-gray-400">Years Retired:</label>
<p class="mt-1 text-md text-white"><%- progress.yearsRetired %></p>
</div>
<div>
<label class="block text-sm font-medium text-gray-400">Total Cost of Retirement:</label>
<p class="mt-1 text-md text-white"><%- new Intl.NumberFormat('en-US', { style: 'currency', currency: geoData.currency ? geoData.currency : 'CAD' }).format(progress.totalCostOfRetirement) %></p>
</div>
<div>
<label class="block text-sm font-medium text-gray-400">Total amount needed (Assets + Retirement Expenses): </label>
<p class="mt-1 text-md text-white"><%- new Intl.NumberFormat('en-US', { style: 'currency', currency: geoData.currency ? geoData.currency : 'CAD' }).format(progress.totalCostOfRetirement + progress.monthlyInvestment * progress.monthsUntilRetirement) %></p>
</div>
</div>
</div>
<div class="border-t border-gray-700 pt-6 space-y-4">
<h4 class="text-lg font-semibold text-gray-300 mb-3">Suggestions:</h4>
<div class="mt-6 p-4 border rounded-lg bg-gray-700 border-gray-600">
<p class="text-sm text-blue-300">
<%= suggestions %>
</p>
</div>
</div>
<div class="flex items-center justify-center space-x-2">
<button type="button" class="py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 cursor-pointer" onclick="editPlan('<%= plan._id %>')">Edit Plan</button>
<button type="button" class="py-2 px-4 border border-red-600 rounded-md shadow-sm text-sm font-medium text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 cursor-pointer" onclick="deletePlan('<%= plan._id %>')">Delete Plan</button>
</div>
</div>
</main>
<script src="/static/scripts/planManager.js"></script>
<%- include("./partials/navBar") %>
<%- include("./partials/scriptLoader") %>
<%- include("./partials/footer") %>
Loaded 100 of 106 files, more files were not shown because too many files have changed in this diff. Show more