diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e940cbf --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +dist/ +*.tsbuildinfo +*.local +.env +.DS_Store diff --git a/card_data.json b/card_data.json new file mode 100644 index 0000000..0c41fb6 --- /dev/null +++ b/card_data.json @@ -0,0 +1,62 @@ +[ + {"id":1,"name":"Gift of Water","action_text":"Make someone happy and bring fresh them water from the well, tank, or tap today.","description":"Sharing water reminds us that clean water is a treasure that everyone in our community should have.","ae":[9],"mfl":[5],"sdgs":[3,6]}, + {"id":2,"name":"Grow Greens","action_text":"Tasty sprouts & herbs. Get seeds, plant in soil, water, care. Grow and snip fresh greens.","description":"Growing your own food is a superpower that makes you more independent and gives you the freshest snacks.","ae":[1,9],"mfl":[2,3],"sdgs":[2,3]}, + {"id":3,"name":"Fine Dining Compost Dish","action_text":"Style food scraps for composting like a fine dining dish and offer them to Mother Earth.","description":"When we compost, we are showing respect to 'Mother Earth' by giving back the energy we didn't use.","ae":[1,2],"mfl":[1,9],"sdgs":[12,13]}, + {"id":4,"name":"Buzzing with Wisdom","action_text":"Learn about bees – their lives, their rhythms, and their ancient connection to plants and ecosystems. Memorize one fact and share it with others.","description":"Understanding bees is important because they are the 'connectors' that help plants grow and keep our world alive.","ae":[4,5],"mfl":[5,7],"sdgs":[13,15]}, + {"id":5,"name":"Baking Traditional Bread","action_text":"Traditional bread or flatbread is full of life force and taste. Try using flour of indigenous or other nutritious crops to bake your favorite bread this week.","description":"Using local crops keeps our traditions alive and fills our bodies with the 'life force' of our own land.","ae":[8,9],"mfl":[3,6],"sdgs":[2,3]}, + {"id":6,"name":"Mixed Waste-monster","action_text":"Food waste all mixed together is useless. Identify the different waste types, sort them, and find ways to reduce & recycle this week.","description":"Sorting trash stops it from becoming a 'monster' and turns it into useful things we can use again.","ae":[1,3],"mfl":[5,9],"sdgs":[12,13]}, + {"id":7,"name":"Ingredient Detective","action_text":"Flip the package, read every ingredient, learn what it is and where it comes from.","description":"Being a detective helps you choose food that is good for your body and fair to the people who made it.","ae":[8,13],"mfl":[3,10],"sdgs":[3,12]}, + {"id":8,"name":"Mindful Eating","action_text":"Smell, touch, feel, savor each bite consciously. Actively appreciate how food nourishes and sustains us.","description":"Slowing down to enjoy food helps us appreciate all the hard work nature and farmers did to feed us.","ae":[9,11],"mfl":[3],"sdgs":[3,12]}, + {"id":9,"name":"Fermentastic","action_text":"Discover the art of fermentation – a natural and cheap way to preserve and enrich ingredients. Learn the basics and set up your first jar.","description":"Fermenting is a clever, low-cost way to make food last longer and become even better for your tummy.","ae":[1,7],"mfl":[3,8],"sdgs":[3,12]}, + {"id":10,"name":"Tree Planter","action_text":"Plant a tree in your compound, school, or in degraded spaces. Join hands with others to learn more and have fun.","description":"Planting a tree is a gift to the future; it helps fix the land and gives the Earth a chance to breathe.","ae":[5,8],"mfl":[4,5],"sdgs":[7,13]}, + {"id":11,"name":"Seed Scout","action_text":"Scout for seeds in your surroundings. Where can you get them? Shops, farms, schools, neighbors? Then obtain some and start saving seeds.","description":"Learning where to find and save seeds is the first step to making sure your community always has enough food to grow.","ae":[5,12],"mfl":[6],"sdgs":[1,2]}, + {"id":12,"name":"Natural Spritz","action_text":"Spraying chemicals kills pests but harms the environment, beneficial insects, and health. Prepare and use a natural biopesticide and see how it works wonders and protects people and the environment.","description":"Using natural sprays proves we can protect our gardens without hurting ourselves or the helpful bugs.","ae":[2,4],"mfl":[1,2],"sdgs":[9,12]}, + {"id":13,"name":"Dashboard Designer","action_text":"Design and paint a 'data dashboard' which shows information about something that influences how well your family's crops grow (which seed, which management, which inputs, etc.).","description":"Tracking your 'data' helps you become a smarter farmer because you can see exactly what makes your plants happy.","ae":[8],"mfl":[10],"sdgs":[8,9]}, + {"id":14,"name":"Onion Bodyguard","action_text":"Ever heard of the superpower of onions? Plant some onions between other plants and observe their protection pester powers.","description":"This shows how different plants can look out for each other, using 'bodyguard' plants to keep pests away naturally.","ae":[2,6],"mfl":[1,5],"sdgs":[13,15]}, + {"id":15,"name":"Local Investor","action_text":"Buy food from a local farm or market today instead of going to a supermarket!","description":"Buying from neighbors keeps your community's money close to home and supports local families.","ae":[7,9],"mfl":[3,8],"sdgs":[1,11]}, + {"id":16,"name":"Poster Designer","action_text":"Create posters about why everyone has the right to be included in farming, owning land, and attending community meetings.","description":"Using art to speak up about land rights helps everyone understand that they have a fair place in the world of farming","ae":[12,13],"mfl":[7,11],"sdgs":[5,10]}, + {"id":17,"name":"Change-Maker","action_text":"Find a topic you are passionate about, join a group that cares about the same, and contribute to making a difference!","description":"Joining a group shows that when we work together on things we care about, our small actions turn into big changes.","ae":[8,13],"mfl":[11,12],"sdgs":[16,17]}, + {"id":18,"name":"Waste Remover","action_text":"Clean up, sort, and recycle the waste you find in your surroundings to help keep animals, plants, land, and water healthy.","description":"Cleaning up trash keeps our land and water healthy for the animals and plants we share our home with.","ae":[1,12],"mfl":[5],"sdgs":[6,13]}, + {"id":19,"name":"Manure Maker","action_text":"Learn how to 'cool down' animal waste before adding it to your compost pile to create super-food for your plants!","description":"Turning waste into 'super-food' for plants is the best way to recycle energy back into the soil.","ae":[3,6],"mfl":[1,9],"sdgs":[12,15]}, + {"id":20,"name":"Mixed Garden","action_text":"Plant maize, beans, leafy greens, forages, and more. Learn about companion planting and that a mixed garden means more food, more soil nutrients, and a healthier family.","description":"A garden with many types of plants is stronger, healthier, and provides a diversity of vitamins for your family.","ae":[4,5],"mfl":[2,5],"sdgs":[2,15]}, + {"id":21,"name":"Ash Bug Blaster","action_text":"Use wood ash to fight bugs naturally. No chemicals needed — ash is free, safe, and it works!","description":"It's important that we can protect our food using simple, free things from our own homes, like wood ash.","ae":[2,6],"mfl":[1,5],"sdgs":[12,15]}, + {"id":22,"name":"Dry & Store","action_text":"Dry your seeds and store them safely. Dry seeds last longer and grow better — patience now means abundance later.","description":"Proper drying and storage is a smart way to protect your seeds so they stay healthy and ready to sprout when the next season comes.","ae":[5,9],"mfl":[6],"sdgs":[2,15]}, + {"id":23,"name":"Seed Distributor","action_text":"Collect ripe fruits and share the seeds with local farmers to spread the harvest further.","description":"Sharing seeds with other farmers is a great way to spread the harvest and make sure everyone in the village can grow good food.","ae":[10,11],"mfl":[6,8],"sdgs":[12,15]}, + {"id":24,"name":"River Cleaners","action_text":"Organize a river clean-up day. A clean river means clean water for animals, plants, and the whole community.","description":"A clean river is like a healthy heartbeat for a community, providing safe water for everyone","ae":[8,12],"mfl":[11,12],"sdgs":[6,14]}, + {"id":25,"name":"Garden Documenter","action_text":"Use a smartphone or tablet camera. Take pictures of all the plants growing in the school garden in a month. Create a slideshow and present to your classmates.","description":"Using cameras and technology to track your garden helps you see the 'big picture' of how nature changes over time.","ae":[8],"mfl":[10],"sdgs":[4,9]}, + {"id":26,"name":"Local Variety Guardian","action_text":"Save seeds from traditional crops. Local varieties are often stronger, more nutritious, and part of your community's food heritage — protect them.","description":"Saving seeds from traditional crops protects your community's history and keeps local plants strong and healthy.","ae":[5,9],"mfl":[6],"sdgs":[2,3]}, + {"id":27,"name":"Nature Regenerator","action_text":"Let nature grow back. Protect young trees and shrubs that appear naturally — farmer-managed regeneration works with nature, not against it.","description":"Sometimes the best way to help nature is to simply protect what is already trying to grow back on its own.","ae":[5,6],"mfl":[4,5],"sdgs":[7,13]}, + {"id":28,"name":"Biodiversity Steward","action_text":"Protect wild places. Some land should stay free from human activity — forests, wetlands, and riverbanks are home to animals and plants that we all and the earth need.","description":"Protecting wild places ensures that nature has its own safe space to thrive without being disturbed.","ae":[4,5],"mfl":[5,7],"sdgs":[13,14]}, + {"id":29,"name":"Land Negotiator","action_text":"When different people want to use the same land for different things, think together about strategies that will help develop a plan that works for everyone.","description":"Learning how to talk through disagreements about land helps create a plan that is fair and peaceful for everyone.","ae":[12,13],"mfl":[7,11],"sdgs":[5,16]}, + {"id":30,"name":"TJD Booster","action_text":"If you played out 5 cards and action missions this week, reward yourself the World Class Changemaker achievement","description":"Celebrating your hard work as a 'Changemaker' gives you the energy and pride to keep helping your community and the planet.","ae":[13],"mfl":[11],"sdgs":[17]}, + {"id":31,"name":"Kitchen Garden","action_text":"Plant a fruit, vegetable, or herb outside your home. Water every day and watch it grow. Fresh food right at your door—save money and eat well!","description":"Having a garden right outside your door means you always have healthy food that costs nothing but a little care.","ae":[2,3],"mfl":[2,3],"sdgs":[1,11]}, + {"id":32,"name":"Balanced Plate Builder","action_text":"Look at what you eat today and sort your food into groups: Carbs, proteins, dairy, fruits & vegetables, and fats. Find out what each group does for your body. Tomorrow, try to build a more balanced plate, to help your body grow strong and stay healthy!","description":"Learning how to balance your plate is the key to building a body that is strong enough to change the world.","ae":[9],"mfl":[3],"sdgs":[2,3]}, + {"id":33,"name":"Indigenous Tree Lover","action_text":"Ask a family or community member about an indigenous tree in your area and all its benefits. Ask about methods to protect them, find an indigenous tree and out your new skills into practice.","description":"Protecting local trees using wisdom from your family keeps the balance of nature in your area healthy.","ae":[5,6],"mfl":[4,5],"sdgs":[13,15]}, + {"id":34,"name":"Cover Crop Protector","action_text":"Cover crops are natural protectors. They protect from the sun, preserve moisture, and keep the soil alive. Plant a cover crop in your school or home garden, such as pumpkins, 9 or cowpeas.","description":"These plants act like a 'living blanket' for the soil, keeping it cool, wet, and full of life.","ae":[3,6],"mfl":[1,2],"sdgs":[13,15]}, + {"id":35,"name":"Local Food Producer","action_text":"Find one food your community usually buys from far away (e.g., flour, fruits, oil, jam). Make a plan: How could your school or family produce this locally? What would you need? Who would you sell to?","description":"Making things locally reduces the need for big trucks and long trips, which is better for the planet.","ae":[7,11],"mfl":[8,11],"sdgs":[8,12]}, + {"id":36,"name":"Market Price Checker","action_text":"Visit a local market and check the price of 3 vegetables. Compare prices at the market vs. at a supermarket. Discuss with your friend where you and the producers get a fairer deal, and why.","description":"Checking prices helps you understand how to get a fair deal for both the person growing the food and the person buying it.","ae":[10,11],"mfl":[8,10],"sdgs":[1,8]}, + {"id":37,"name":"Community Meeting","action_text":"When people learn and decide together, everyone grows stronger. Plan a class or community meeting about a food or farming topic. Ensure everyone has a chance to speak. Write down the key ideas and decisions.","description":"Making sure everyone gets a chance to speak at meetings ensures that big decisions are fair and include everyone's best ideas.","ae":[8,13],"mfl":[11],"sdgs":[5,10]}, + {"id":38,"name":"Jar Farm Product Marketer","action_text":"Grow sprouts in a jar for a week, invent a product name, design a label and 'market' your healthy food ingredient to family, friends and neighbors.","description":"Learning how to label and 'sell' healthy food teaches you the business skills needed to be a successful green entrepreneur.","ae":[2,7],"mfl":[2,8],"sdgs":[1,9]}, + {"id":39,"name":"Food Donation Hero","action_text":"Everybody should have access to healthy and yummy food. If you can, share some food with someone who needs it today. It can be small, every act matters.","description":"Sharing food ensures that 'healthy and yummy' meals aren't a luxury, but something everyone can enjoy.","ae":[10],"mfl":[3],"sdgs":[2,10]}, + {"id":40,"name":"Windbreak Warrior","action_text":"Trees are powerful protectors for people and farms. Stand under a tree and feel what it does. Think about how it blocks wind, gives shade, and protects both you and your crops?","description":"Trees are like giant shields that protect our homes and crops from being damaged by the wind and sun.","ae":[5,6],"mfl":[4,7],"sdgs":[7,15]}, + {"id":41,"name":"Seed Champion","action_text":"Every person who saves seeds makes the whole community stronger, and more independent. Help someone understand why saving and sharing seeds matters.","description":"When you teach others to save seeds, you help your whole community become more independent and powerful.","ae":[5,10],"mfl":[6,12],"sdgs":[1,10]}, + {"id":42,"name":"Fruit & Tree Team","action_text":"Nature works together. Try planting or finding a climbing fruit next to a tree. Watch how the tree supports the fruit, and how they live together happily.","description":"This shows how nature loves teamwork; trees can provide the 'ladder' for fruits to climb and grow better.","ae":[5,6],"mfl":[4,7],"sdgs":[13,15]}, + {"id":43,"name":"Manure Cycle Drawer","action_text":"Animals make manure. The nutrients in the manure feed crops. Crops feed people. Draw this farm nutrient cycle and show how everything is connected!","description":"Drawing this cycle helps us see how animals, plants, and people are all part of one big, connected family.","ae":[4,6],"mfl":[1,7],"sdgs":[13,15]}, + {"id":44,"name":"Animal Hero","action_text":"Think about a farm with animals, crops, trees and people together. How do animals help the farm? Tell a friend about all the ways!","description":"Sharing the story of how animals help the farm helps others see that every creature has an important job to do in nature.","ae":[4,6],"mfl":[1,7],"sdgs":[15]}, + {"id":45,"name":"Land Magic","action_text":"One piece of land can do so many things! Choose a piece of land (home, school, or a nearby farm), and list everything it already provides for people and nature. Now think about 3 more things it could do. Draw a map and share it with your friends.","description":"Mapping all the things one piece of land can do helps us discover new ways to grow food and protect nature at the same time.","ae":[5,6],"mfl":[7],"sdgs":[13,15]}, + {"id":46,"name":"Local Chicken Lover","action_text":"Find a member of the local community who raises and sells local chickens. Ask them what they love about them!","description":"Supporting local chicken farmers helps keep your community's economy strong and respects animals that belong in your environment.","ae":[6,7],"mfl":[1,8],"sdgs":[1,8]}, + {"id":47,"name":"Food Dryer Explorer","action_text":"Choose a fruit or vegetable that your home sometimes has too much of (e.g. mangoes, tomatoes). Find out how to dry and preserve it, then try it out!","description":"Drying food is a smart way to make sure that 'too much' food today becomes a great snack for tomorrow.","ae":[1,7],"mfl":[3,9],"sdgs":[9,12]}, + {"id":48,"name":"Scrap-tastic Animal Feeder","action_text":"Turn waste into feeds — nothing goes to waste! Collect food scraps and give them to animals (like chickens). What scraps can they eat? What should not be fed? Write down a scrap-tastic menu for them.","description":"Feeding animals the right scraps means nothing is ever wasted on a smart, circular farm.","ae":[1,4],"mfl":[1,9],"sdgs":[11,15]}, + {"id":49,"name":"Leftover Chef","action_text":"Say no to food waste! Use yesterday's leftovers to make a meal. How can you make it tasty again?","description":"Being a leftover chef is a creative way to say 'no' to waste and 'yes' to delicious, recycled meals.","ae":[1,9],"mfl":[3,9],"sdgs":[2,12]}, + {"id":50,"name":"Weather Tracker Checker","action_text":"Check the weather each day for a week using a phone or computer. Tip: Planting at the right time saves crops and makes your harvest bigger!","description":"Checking the weather helps you become a 'science-based' farmer, so you can plant at the perfect time to get a big harvest.","ae":[8,12],"mfl":[10],"sdgs":[4,9]}, + {"id":51,"name":"Farm Record Keeper","action_text":"Good records help you understand your farm better. Record information about 3 plants in your farm (at home or at school): planting time, management, pests, changes in color, growth etc. Think about the data you need to make good farming decision.","description":"Writing down facts about your plants helps you understand what they need to grow best, making you a much smarter farmer.","ae":[8,12],"mfl":[10],"sdgs":[4,9]}, + {"id":52,"name":"The Water Fertilizers","action_text":"Did you know that fish are little wizards? They make for yummy meals, but also fertilize the water they live in that can be reused to water your crops. Discuss with your friends if you can set up a fish pond at school.","description":"This shows how we can use nature's own 'magic' (like fish) to help our food grow without needing chemicals","ae":[6,7],"mfl":[1,7],"sdgs":[1,14]}, + {"id":53,"name":"Small Farm, Big Voice","action_text":"Even small farms matter! Everything is connected. Challenge: Roleplay a farmer at a community meeting and speak up for sustainable land use. What would you say?","description":"Even if a farm is small, speaking up for sustainable land use is a powerful way to protect the future of food.","ae":[12,13],"mfl":[7,11],"sdgs":[5,10]}, + {"id":54,"name":"Nature Educator","action_text":"When people understand, they act. Teach someone in your community why and how to protect nature, save forests, animals, and rivers!","description":"Teaching others how to protect forests and rivers spreads a 'green message' that can save the environment for everyone.","ae":[8,13],"mfl":[5,12],"sdgs":[10,15]}, + {"id":55,"name":"Teamwork Harvest","action_text":"Plant and grow something with 1 or 2 friends. Teamwork makes your work easier and your harvest bigger. Many hands, full baskets — and full hearts!","description":"Doing things with friends makes the work lighter and the celebration of the harvest much bigger!","ae":[8,13],"mfl":[2,11],"sdgs":[8,15]}, + {"id":56,"name":"Sky Garden","action_text":"Food can grow almost anywhere. Try growing upwards by planting in a hanging basket! More food with less space. Your garden can reach the sky!","description":"Growing upwards proves that you don't need a huge farm to be a successful gardener—you just need a little creativity!","ae":[7,9],"mfl":[2,7],"sdgs":[9,10]}, + {"id":57,"name":"Write for Change","action_text":"Write to a local leader about a food system issue you care about. Be precise, present evidence, make suggestions for change, offer support.","description":"Writing to leaders with real evidence shows them that young people have the power and the facts to help change the world.","ae":[12,13],"mfl":[11,12],"sdgs":[10,16]}, + {"id":58,"name":"Ancient Wisdom Defender","action_text":"Old knowledge is gold! Talk to an elder about farming and food. Learn from their stories!","description":"Talking to elders is like finding a treasure chest of secrets that can help us farm better today.","ae":[8,9],"mfl":[11,12],"sdgs":[5,10]}, + {"id":59,"name":"Seedling Protector","action_text":"Visit a tree nursery. Learn how you can join hands to grow a tree step-by-step from selecting a good seed to planting, and helping it grow.","description":"Learning how to care for tiny trees in a nursery ensures they grow up strong enough to protect the environment.","ae":[6,12],"mfl":[4,11],"sdgs":[7,15]}, + {"id":60,"name":"Cooperative Builder","action_text":"Every voice counts — but there is power in numbers. When farmers work together and sell as a group, they earn more and waste less. Find ways to improve access to healthy food in your area.","description":"When we work together as a group, we have a much louder voice and can solve bigger problems.","ae":[10,11],"mfl":[8,9],"sdgs":[10,11]} +] diff --git a/cards/1-Gift-of-Water-Action.png b/cards/1-Gift-of-Water-Action.png new file mode 100644 index 0000000..2823d96 Binary files /dev/null and b/cards/1-Gift-of-Water-Action.png differ diff --git a/cards/10 - Tree Planter.png b/cards/10 - Tree Planter.png new file mode 100644 index 0000000..f6c668c Binary files /dev/null and b/cards/10 - Tree Planter.png differ diff --git a/cards/11 - Seed Scout.png b/cards/11 - Seed Scout.png new file mode 100644 index 0000000..e4efc21 Binary files /dev/null and b/cards/11 - Seed Scout.png differ diff --git a/cards/12 - Natural Spritz.png b/cards/12 - Natural Spritz.png new file mode 100644 index 0000000..2b22fd6 Binary files /dev/null and b/cards/12 - Natural Spritz.png differ diff --git a/cards/13 - Dashboard Designer.png b/cards/13 - Dashboard Designer.png new file mode 100644 index 0000000..288df48 Binary files /dev/null and b/cards/13 - Dashboard Designer.png differ diff --git a/cards/14 - Onion Bodyguard.png b/cards/14 - Onion Bodyguard.png new file mode 100644 index 0000000..0429765 Binary files /dev/null and b/cards/14 - Onion Bodyguard.png differ diff --git a/cards/15 - Local Investor.png b/cards/15 - Local Investor.png new file mode 100644 index 0000000..71cc200 Binary files /dev/null and b/cards/15 - Local Investor.png differ diff --git a/cards/16 - Poster Designer.png b/cards/16 - Poster Designer.png new file mode 100644 index 0000000..da50a72 Binary files /dev/null and b/cards/16 - Poster Designer.png differ diff --git a/cards/17 - Change-Maker.png b/cards/17 - Change-Maker.png new file mode 100644 index 0000000..1aebb0e Binary files /dev/null and b/cards/17 - Change-Maker.png differ diff --git a/cards/18 - Waste Remover.png b/cards/18 - Waste Remover.png new file mode 100644 index 0000000..00b4cf4 Binary files /dev/null and b/cards/18 - Waste Remover.png differ diff --git a/cards/19 - Manure Maker.png b/cards/19 - Manure Maker.png new file mode 100644 index 0000000..51ed796 Binary files /dev/null and b/cards/19 - Manure Maker.png differ diff --git a/cards/2-Grow-Greens-Action.png b/cards/2-Grow-Greens-Action.png new file mode 100644 index 0000000..d0a0438 Binary files /dev/null and b/cards/2-Grow-Greens-Action.png differ diff --git a/cards/20 - Mixed Garden.png b/cards/20 - Mixed Garden.png new file mode 100644 index 0000000..aa28675 Binary files /dev/null and b/cards/20 - Mixed Garden.png differ diff --git a/cards/21 - Ash Bug Blaster.png b/cards/21 - Ash Bug Blaster.png new file mode 100644 index 0000000..9cd8e53 Binary files /dev/null and b/cards/21 - Ash Bug Blaster.png differ diff --git a/cards/22 - Dry & Store.png b/cards/22 - Dry & Store.png new file mode 100644 index 0000000..94d9cbb Binary files /dev/null and b/cards/22 - Dry & Store.png differ diff --git a/cards/23 - Seed Distributor.png b/cards/23 - Seed Distributor.png new file mode 100644 index 0000000..705729a Binary files /dev/null and b/cards/23 - Seed Distributor.png differ diff --git a/cards/24 - River Cleaners.png b/cards/24 - River Cleaners.png new file mode 100644 index 0000000..8505c86 Binary files /dev/null and b/cards/24 - River Cleaners.png differ diff --git a/cards/25 - Garden Documenter.png b/cards/25 - Garden Documenter.png new file mode 100644 index 0000000..b0b2aba Binary files /dev/null and b/cards/25 - Garden Documenter.png differ diff --git a/cards/26 - Local Variety Guardian.png b/cards/26 - Local Variety Guardian.png new file mode 100644 index 0000000..c96c45e Binary files /dev/null and b/cards/26 - Local Variety Guardian.png differ diff --git a/cards/27 - Nature Regenerator.png b/cards/27 - Nature Regenerator.png new file mode 100644 index 0000000..379044b Binary files /dev/null and b/cards/27 - Nature Regenerator.png differ diff --git a/cards/28 - Biodiversity Steward.png b/cards/28 - Biodiversity Steward.png new file mode 100644 index 0000000..f61823e Binary files /dev/null and b/cards/28 - Biodiversity Steward.png differ diff --git a/cards/29 - Land Negotiator.png b/cards/29 - Land Negotiator.png new file mode 100644 index 0000000..1d3e5f7 Binary files /dev/null and b/cards/29 - Land Negotiator.png differ diff --git a/cards/3-Fine-Dining-Compost-Dish-Action.png b/cards/3-Fine-Dining-Compost-Dish-Action.png new file mode 100644 index 0000000..6619c59 Binary files /dev/null and b/cards/3-Fine-Dining-Compost-Dish-Action.png differ diff --git a/cards/30-TJD-Booster-Action.png b/cards/30-TJD-Booster-Action.png new file mode 100644 index 0000000..8e8b661 Binary files /dev/null and b/cards/30-TJD-Booster-Action.png differ diff --git a/cards/31 - Kitchen Garden.png b/cards/31 - Kitchen Garden.png new file mode 100644 index 0000000..0b8a37c Binary files /dev/null and b/cards/31 - Kitchen Garden.png differ diff --git a/cards/32 - Balanced Plate Builder.png b/cards/32 - Balanced Plate Builder.png new file mode 100644 index 0000000..c72b498 Binary files /dev/null and b/cards/32 - Balanced Plate Builder.png differ diff --git a/cards/33 - Indigenous Tree Lover.png b/cards/33 - Indigenous Tree Lover.png new file mode 100644 index 0000000..a869f0c Binary files /dev/null and b/cards/33 - Indigenous Tree Lover.png differ diff --git a/cards/34 - Cover Crop Protector.png b/cards/34 - Cover Crop Protector.png new file mode 100644 index 0000000..7c8b152 Binary files /dev/null and b/cards/34 - Cover Crop Protector.png differ diff --git a/cards/35 - Local Food Producer.png b/cards/35 - Local Food Producer.png new file mode 100644 index 0000000..b8ea254 Binary files /dev/null and b/cards/35 - Local Food Producer.png differ diff --git a/cards/36 - Market Price Checker.png b/cards/36 - Market Price Checker.png new file mode 100644 index 0000000..c1b9916 Binary files /dev/null and b/cards/36 - Market Price Checker.png differ diff --git a/cards/37 - Community Meeting.png b/cards/37 - Community Meeting.png new file mode 100644 index 0000000..b71ad12 Binary files /dev/null and b/cards/37 - Community Meeting.png differ diff --git a/cards/38 - Jar Farm Product Marketer.png b/cards/38 - Jar Farm Product Marketer.png new file mode 100644 index 0000000..a14b406 Binary files /dev/null and b/cards/38 - Jar Farm Product Marketer.png differ diff --git a/cards/39 - Food Donation Hero.png b/cards/39 - Food Donation Hero.png new file mode 100644 index 0000000..b1b5b38 Binary files /dev/null and b/cards/39 - Food Donation Hero.png differ diff --git a/cards/4-Buzzing-with-Wisdom-Action.png b/cards/4-Buzzing-with-Wisdom-Action.png new file mode 100644 index 0000000..c2746de Binary files /dev/null and b/cards/4-Buzzing-with-Wisdom-Action.png differ diff --git a/cards/40 - Windbreak Warrior.png b/cards/40 - Windbreak Warrior.png new file mode 100644 index 0000000..f524f47 Binary files /dev/null and b/cards/40 - Windbreak Warrior.png differ diff --git a/cards/41 - Seed Champion.png b/cards/41 - Seed Champion.png new file mode 100644 index 0000000..48d565b Binary files /dev/null and b/cards/41 - Seed Champion.png differ diff --git a/cards/42 - Fruit & Tree Team.png b/cards/42 - Fruit & Tree Team.png new file mode 100644 index 0000000..640f5a5 Binary files /dev/null and b/cards/42 - Fruit & Tree Team.png differ diff --git a/cards/43 - Manure Cycle Drawer.png b/cards/43 - Manure Cycle Drawer.png new file mode 100644 index 0000000..ee7faa4 Binary files /dev/null and b/cards/43 - Manure Cycle Drawer.png differ diff --git a/cards/44 - Animal Hero.png b/cards/44 - Animal Hero.png new file mode 100644 index 0000000..77af5a0 Binary files /dev/null and b/cards/44 - Animal Hero.png differ diff --git a/cards/45 - Land Magic.png b/cards/45 - Land Magic.png new file mode 100644 index 0000000..c6ff021 Binary files /dev/null and b/cards/45 - Land Magic.png differ diff --git a/cards/46 - Local Chicken Lover.png b/cards/46 - Local Chicken Lover.png new file mode 100644 index 0000000..c6182a5 Binary files /dev/null and b/cards/46 - Local Chicken Lover.png differ diff --git a/cards/47 - Food Dryer Explorer.png b/cards/47 - Food Dryer Explorer.png new file mode 100644 index 0000000..7caebc2 Binary files /dev/null and b/cards/47 - Food Dryer Explorer.png differ diff --git a/cards/48 - Scrap-tastic Animal Feeder.png b/cards/48 - Scrap-tastic Animal Feeder.png new file mode 100644 index 0000000..cc10081 Binary files /dev/null and b/cards/48 - Scrap-tastic Animal Feeder.png differ diff --git a/cards/49 - Leftover Chef.png b/cards/49 - Leftover Chef.png new file mode 100644 index 0000000..8a41e9d Binary files /dev/null and b/cards/49 - Leftover Chef.png differ diff --git a/cards/5-Baking-Flatbread.png b/cards/5-Baking-Flatbread.png new file mode 100644 index 0000000..274e2c4 Binary files /dev/null and b/cards/5-Baking-Flatbread.png differ diff --git a/cards/50 - Weather Tracker Checker.png b/cards/50 - Weather Tracker Checker.png new file mode 100644 index 0000000..05c5568 Binary files /dev/null and b/cards/50 - Weather Tracker Checker.png differ diff --git a/cards/51 - Farm Record Keeper.png b/cards/51 - Farm Record Keeper.png new file mode 100644 index 0000000..10ccd13 Binary files /dev/null and b/cards/51 - Farm Record Keeper.png differ diff --git a/cards/52 - The Water Fertilizers.png b/cards/52 - The Water Fertilizers.png new file mode 100644 index 0000000..589e698 Binary files /dev/null and b/cards/52 - The Water Fertilizers.png differ diff --git a/cards/53 - Small Farm, Big Voice.png b/cards/53 - Small Farm, Big Voice.png new file mode 100644 index 0000000..7e7e193 Binary files /dev/null and b/cards/53 - Small Farm, Big Voice.png differ diff --git a/cards/54 - Nature Educator.png b/cards/54 - Nature Educator.png new file mode 100644 index 0000000..d7780e2 Binary files /dev/null and b/cards/54 - Nature Educator.png differ diff --git a/cards/55 - Teamwork Harvest.png b/cards/55 - Teamwork Harvest.png new file mode 100644 index 0000000..e48331c Binary files /dev/null and b/cards/55 - Teamwork Harvest.png differ diff --git a/cards/56 - Sky Garden.png b/cards/56 - Sky Garden.png new file mode 100644 index 0000000..2b11e14 Binary files /dev/null and b/cards/56 - Sky Garden.png differ diff --git a/cards/57-Be-the-Change-Action.png b/cards/57-Be-the-Change-Action.png new file mode 100644 index 0000000..8f1b57d Binary files /dev/null and b/cards/57-Be-the-Change-Action.png differ diff --git a/cards/58-Ancient-Wisdom-Defender-Action.png b/cards/58-Ancient-Wisdom-Defender-Action.png new file mode 100644 index 0000000..6a98efd Binary files /dev/null and b/cards/58-Ancient-Wisdom-Defender-Action.png differ diff --git a/cards/59-Seedling-Protector-Action.png b/cards/59-Seedling-Protector-Action.png new file mode 100644 index 0000000..2bd3985 Binary files /dev/null and b/cards/59-Seedling-Protector-Action.png differ diff --git a/cards/6-Mixed-Wastemonster-Action.png b/cards/6-Mixed-Wastemonster-Action.png new file mode 100644 index 0000000..e2667ce Binary files /dev/null and b/cards/6-Mixed-Wastemonster-Action.png differ diff --git a/cards/60-Cooperative-Builder-Action.png b/cards/60-Cooperative-Builder-Action.png new file mode 100644 index 0000000..73ea174 Binary files /dev/null and b/cards/60-Cooperative-Builder-Action.png differ diff --git a/cards/7-Ingredient-Detective-Action.png b/cards/7-Ingredient-Detective-Action.png new file mode 100644 index 0000000..53513a5 Binary files /dev/null and b/cards/7-Ingredient-Detective-Action.png differ diff --git a/cards/8-Mindful-Eating-Action.png b/cards/8-Mindful-Eating-Action.png new file mode 100644 index 0000000..803f61e Binary files /dev/null and b/cards/8-Mindful-Eating-Action.png differ diff --git a/cards/9-Fermentastic-Action.png b/cards/9-Fermentastic-Action.png new file mode 100644 index 0000000..eafea24 Binary files /dev/null and b/cards/9-Fermentastic-Action.png differ diff --git a/icons/AE SVG/AE-1.svg b/icons/AE SVG/AE-1.svg new file mode 100644 index 0000000..600e7ad --- /dev/null +++ b/icons/AE SVG/AE-1.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/icons/AE SVG/AE-10.svg b/icons/AE SVG/AE-10.svg new file mode 100644 index 0000000..1619738 --- /dev/null +++ b/icons/AE SVG/AE-10.svg @@ -0,0 +1,337 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/AE SVG/AE-11.svg b/icons/AE SVG/AE-11.svg new file mode 100644 index 0000000..c050d7d --- /dev/null +++ b/icons/AE SVG/AE-11.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/AE SVG/AE-12.svg b/icons/AE SVG/AE-12.svg new file mode 100644 index 0000000..7460dd2 --- /dev/null +++ b/icons/AE SVG/AE-12.svg @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/AE SVG/AE-13.svg b/icons/AE SVG/AE-13.svg new file mode 100644 index 0000000..9b04306 --- /dev/null +++ b/icons/AE SVG/AE-13.svg @@ -0,0 +1,649 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/icons/AE SVG/AE-2.svg b/icons/AE SVG/AE-2.svg new file mode 100644 index 0000000..eda7ef7 --- /dev/null +++ b/icons/AE SVG/AE-2.svg @@ -0,0 +1,585 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/AE SVG/AE-3.svg b/icons/AE SVG/AE-3.svg new file mode 100644 index 0000000..a48051b --- /dev/null +++ b/icons/AE SVG/AE-3.svg @@ -0,0 +1,865 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/icons/AE SVG/AE-4.svg b/icons/AE SVG/AE-4.svg new file mode 100644 index 0000000..44664a0 --- /dev/null +++ b/icons/AE SVG/AE-4.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/icons/AE SVG/AE-5.svg b/icons/AE SVG/AE-5.svg new file mode 100644 index 0000000..09ba99c --- /dev/null +++ b/icons/AE SVG/AE-5.svg @@ -0,0 +1,956 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/AE SVG/AE-6.svg b/icons/AE SVG/AE-6.svg new file mode 100644 index 0000000..b2fec57 --- /dev/null +++ b/icons/AE SVG/AE-6.svg @@ -0,0 +1,750 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/AE SVG/AE-7.svg b/icons/AE SVG/AE-7.svg new file mode 100644 index 0000000..432e646 --- /dev/null +++ b/icons/AE SVG/AE-7.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/icons/AE SVG/AE-8.svg b/icons/AE SVG/AE-8.svg new file mode 100644 index 0000000..db62a8b --- /dev/null +++ b/icons/AE SVG/AE-8.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/icons/AE SVG/AE-9.svg b/icons/AE SVG/AE-9.svg new file mode 100644 index 0000000..4138b56 --- /dev/null +++ b/icons/AE SVG/AE-9.svg @@ -0,0 +1,596 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/MFL/MFL-1.png b/icons/MFL/MFL-1.png new file mode 100644 index 0000000..5b3031b Binary files /dev/null and b/icons/MFL/MFL-1.png differ diff --git a/icons/MFL/MFL-10.png b/icons/MFL/MFL-10.png new file mode 100644 index 0000000..ad6e3f7 Binary files /dev/null and b/icons/MFL/MFL-10.png differ diff --git a/icons/MFL/MFL-11.png b/icons/MFL/MFL-11.png new file mode 100644 index 0000000..4179c1b Binary files /dev/null and b/icons/MFL/MFL-11.png differ diff --git a/icons/MFL/MFL-12.png b/icons/MFL/MFL-12.png new file mode 100644 index 0000000..9a1c1e1 Binary files /dev/null and b/icons/MFL/MFL-12.png differ diff --git a/icons/MFL/MFL-2.png b/icons/MFL/MFL-2.png new file mode 100644 index 0000000..b24c6b5 Binary files /dev/null and b/icons/MFL/MFL-2.png differ diff --git a/icons/MFL/MFL-3.png b/icons/MFL/MFL-3.png new file mode 100644 index 0000000..dabbb62 Binary files /dev/null and b/icons/MFL/MFL-3.png differ diff --git a/icons/MFL/MFL-4.png b/icons/MFL/MFL-4.png new file mode 100644 index 0000000..689bec6 Binary files /dev/null and b/icons/MFL/MFL-4.png differ diff --git a/icons/MFL/MFL-5.png b/icons/MFL/MFL-5.png new file mode 100644 index 0000000..e2fdc8a Binary files /dev/null and b/icons/MFL/MFL-5.png differ diff --git a/icons/MFL/MFL-6.png b/icons/MFL/MFL-6.png new file mode 100644 index 0000000..c4f7f2c Binary files /dev/null and b/icons/MFL/MFL-6.png differ diff --git a/icons/MFL/MFL-7.png b/icons/MFL/MFL-7.png new file mode 100644 index 0000000..9f0e1af Binary files /dev/null and b/icons/MFL/MFL-7.png differ diff --git a/icons/MFL/MFL-8.png b/icons/MFL/MFL-8.png new file mode 100644 index 0000000..85a5167 Binary files /dev/null and b/icons/MFL/MFL-8.png differ diff --git a/icons/MFL/MFL-9.png b/icons/MFL/MFL-9.png new file mode 100644 index 0000000..2408d8d Binary files /dev/null and b/icons/MFL/MFL-9.png differ diff --git a/icons/SDG/SDG-1.png b/icons/SDG/SDG-1.png new file mode 100644 index 0000000..1b1f680 Binary files /dev/null and b/icons/SDG/SDG-1.png differ diff --git a/icons/SDG/SDG-11.png b/icons/SDG/SDG-11.png new file mode 100644 index 0000000..afb9b44 Binary files /dev/null and b/icons/SDG/SDG-11.png differ diff --git a/icons/SDG/SDG-12.png b/icons/SDG/SDG-12.png new file mode 100644 index 0000000..23a3a0d Binary files /dev/null and b/icons/SDG/SDG-12.png differ diff --git a/icons/SDG/SDG-13.png b/icons/SDG/SDG-13.png new file mode 100644 index 0000000..444afa1 Binary files /dev/null and b/icons/SDG/SDG-13.png differ diff --git a/icons/SDG/SDG-14.png b/icons/SDG/SDG-14.png new file mode 100644 index 0000000..659db4e Binary files /dev/null and b/icons/SDG/SDG-14.png differ diff --git a/icons/SDG/SDG-15.png b/icons/SDG/SDG-15.png new file mode 100644 index 0000000..df2f341 Binary files /dev/null and b/icons/SDG/SDG-15.png differ diff --git a/icons/SDG/SDG-16.png b/icons/SDG/SDG-16.png new file mode 100644 index 0000000..de777b0 Binary files /dev/null and b/icons/SDG/SDG-16.png differ diff --git a/icons/SDG/SDG-17.png b/icons/SDG/SDG-17.png new file mode 100644 index 0000000..4672420 Binary files /dev/null and b/icons/SDG/SDG-17.png differ diff --git a/icons/SDG/SDG-2.png b/icons/SDG/SDG-2.png new file mode 100644 index 0000000..af193aa Binary files /dev/null and b/icons/SDG/SDG-2.png differ diff --git a/icons/SDG/SDG-3.png b/icons/SDG/SDG-3.png new file mode 100644 index 0000000..42eb43d Binary files /dev/null and b/icons/SDG/SDG-3.png differ diff --git a/icons/SDG/SDG-4.png b/icons/SDG/SDG-4.png new file mode 100644 index 0000000..2ab8d39 Binary files /dev/null and b/icons/SDG/SDG-4.png differ diff --git a/icons/SDG/SDG-5.png b/icons/SDG/SDG-5.png new file mode 100644 index 0000000..19d1156 Binary files /dev/null and b/icons/SDG/SDG-5.png differ diff --git a/icons/SDG/SDG-6.png b/icons/SDG/SDG-6.png new file mode 100644 index 0000000..40c1c2b Binary files /dev/null and b/icons/SDG/SDG-6.png differ diff --git a/icons/SDG/SDG-7.png b/icons/SDG/SDG-7.png new file mode 100644 index 0000000..2caaa94 Binary files /dev/null and b/icons/SDG/SDG-7.png differ diff --git a/icons/SDG/SDG-8.png b/icons/SDG/SDG-8.png new file mode 100644 index 0000000..e2245d1 Binary files /dev/null and b/icons/SDG/SDG-8.png differ diff --git a/icons/SDG/SDG-9.png b/icons/SDG/SDG-9.png new file mode 100644 index 0000000..0e10540 Binary files /dev/null and b/icons/SDG/SDG-9.png differ diff --git a/icons/SDG/sdg-10.png b/icons/SDG/sdg-10.png new file mode 100644 index 0000000..4020715 Binary files /dev/null and b/icons/SDG/sdg-10.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..54f67cd --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + + + fsys — Card & Value Explorer + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..627efc6 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1876 @@ +{ + "name": "fsys-explorer", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "fsys-explorer", + "version": "1.0.0", + "dependencies": { + "framer-motion": "^12.9.4", + "react": "^19.1.0", + "react-dom": "^19.1.0" + }, + "devDependencies": { + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", + "@vitejs/plugin-react": "^4.4.1", + "typescript": "~5.8.3", + "vite": "^6.3.5" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.3.tgz", + "integrity": "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.3.tgz", + "integrity": "sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.3.tgz", + "integrity": "sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.3.tgz", + "integrity": "sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.3.tgz", + "integrity": "sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.3.tgz", + "integrity": "sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.3.tgz", + "integrity": "sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.3.tgz", + "integrity": "sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.3.tgz", + "integrity": "sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.3.tgz", + "integrity": "sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.3.tgz", + "integrity": "sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.3.tgz", + "integrity": "sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.3.tgz", + "integrity": "sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.3.tgz", + "integrity": "sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.3.tgz", + "integrity": "sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.3.tgz", + "integrity": "sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.3.tgz", + "integrity": "sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.3.tgz", + "integrity": "sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.3.tgz", + "integrity": "sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.3.tgz", + "integrity": "sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.3.tgz", + "integrity": "sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.3.tgz", + "integrity": "sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.3.tgz", + "integrity": "sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.3.tgz", + "integrity": "sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.3.tgz", + "integrity": "sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.29", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.29.tgz", + "integrity": "sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001792", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", + "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.353", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.353.tgz", + "integrity": "sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/framer-motion": { + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.38.0.tgz", + "integrity": "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.38.0", + "motion-utils": "^12.36.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/motion-dom": { + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz", + "integrity": "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.36.0" + } + }, + "node_modules/motion-utils": { + "version": "12.36.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.36.0.tgz", + "integrity": "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", + "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", + "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.6" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.3.tgz", + "integrity": "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.3", + "@rollup/rollup-android-arm64": "4.60.3", + "@rollup/rollup-darwin-arm64": "4.60.3", + "@rollup/rollup-darwin-x64": "4.60.3", + "@rollup/rollup-freebsd-arm64": "4.60.3", + "@rollup/rollup-freebsd-x64": "4.60.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.3", + "@rollup/rollup-linux-arm-musleabihf": "4.60.3", + "@rollup/rollup-linux-arm64-gnu": "4.60.3", + "@rollup/rollup-linux-arm64-musl": "4.60.3", + "@rollup/rollup-linux-loong64-gnu": "4.60.3", + "@rollup/rollup-linux-loong64-musl": "4.60.3", + "@rollup/rollup-linux-ppc64-gnu": "4.60.3", + "@rollup/rollup-linux-ppc64-musl": "4.60.3", + "@rollup/rollup-linux-riscv64-gnu": "4.60.3", + "@rollup/rollup-linux-riscv64-musl": "4.60.3", + "@rollup/rollup-linux-s390x-gnu": "4.60.3", + "@rollup/rollup-linux-x64-gnu": "4.60.3", + "@rollup/rollup-linux-x64-musl": "4.60.3", + "@rollup/rollup-openbsd-x64": "4.60.3", + "@rollup/rollup-openharmony-arm64": "4.60.3", + "@rollup/rollup-win32-arm64-msvc": "4.60.3", + "@rollup/rollup-win32-ia32-msvc": "4.60.3", + "@rollup/rollup-win32-x64-gnu": "4.60.3", + "@rollup/rollup-win32-x64-msvc": "4.60.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", + "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..0218323 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "fsys-explorer", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "framer-motion": "^12.9.4", + "react": "^19.1.0", + "react-dom": "^19.1.0" + }, + "devDependencies": { + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", + "@vitejs/plugin-react": "^4.4.1", + "typescript": "~5.8.3", + "vite": "^6.3.5" + } +} diff --git a/public/cards b/public/cards new file mode 120000 index 0000000..5a24cd9 --- /dev/null +++ b/public/cards @@ -0,0 +1 @@ +../cards \ No newline at end of file diff --git a/public/icons b/public/icons new file mode 120000 index 0000000..3cfd4cb --- /dev/null +++ b/public/icons @@ -0,0 +1 @@ +../icons \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..34c09ed --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,87 @@ +import { useState, useMemo, useCallback, useEffect } from 'react'; +import type { CardData } from './types'; +import cardDataRaw from '../card_data.json?raw'; +import { AE_LABELS, MFL_LABELS, SDG_LABELS } from './data/labels'; +import IconBar from './components/IconBar'; +import IconColumn from './components/IconColumn'; +import CardField from './components/CardField'; +import CardFocusOverlay from './components/CardFocusOverlay'; +import './styles/app.css'; + +const cards: CardData[] = JSON.parse(cardDataRaw); + +const AE_COUNT = 13; +const MFL_COUNT = 12; +const SDG_COUNT = 17; + +function matchesAll(card: CardData, selectedAE: Set, selectedMFL: Set, selectedSDG: Set): boolean { + for (const v of selectedAE) if (!card.ae.includes(v)) return false; + for (const v of selectedMFL) if (!card.mfl.includes(v)) return false; + for (const v of selectedSDG) if (!card.sdgs.includes(v)) return false; + return true; +} + +export default function App() { + const [selectedAE, setSelectedAE] = useState>(new Set()); + const [selectedMFL, setSelectedMFL] = useState>(new Set()); + const [selectedSDG, setSelectedSDG] = useState>(new Set()); + const [focusedCardId, setFocusedCardId] = useState(null); + + useEffect(() => { + const handler = (e: KeyboardEvent) => { + if (e.key === 'Escape') setFocusedCardId(null); + }; + window.addEventListener('keydown', handler); + return () => window.removeEventListener('keydown', handler); + }, []); + + const toggleSet = useCallback((value: number, set: Set, setter: (s: Set) => void) => { + const next = new Set(set); + if (next.has(value)) next.delete(value); + else next.add(value); + setter(next); + setFocusedCardId(null); + }, []); + + const filteredCards = useMemo(() => { + const total = selectedAE.size + selectedMFL.size + selectedSDG.size; + if (total === 0) return []; + return cards.filter((c) => matchesAll(c, selectedAE, selectedMFL, selectedSDG)); + }, [selectedAE, selectedMFL, selectedSDG]); + + const active = { ae: selectedAE.size > 0, mfl: selectedMFL.size > 0, sdg: selectedSDG.size > 0 }; + + const handleFocus = useCallback((id: number | null) => { + setFocusedCardId(id); + }, []); + + const focusedCard = focusedCardId !== null ? cards.find((c) => c.id === focusedCardId) ?? null : null; + + return ( +
+ {focusedCard && setFocusedCardId(null)} />} + +
+ toggleSet(v, selectedAE, setSelectedAE)} /> +
+ +
+ toggleSet(v, selectedSDG, setSelectedSDG)} /> +
+ +
+ toggleSet(v, selectedMFL, setSelectedMFL)} /> +
+ +
+
+ {active.ae ? 'AE' : ''} + {active.mfl ? 'MFL' : ''} + {active.sdg ? 'SDG' : ''} + {filteredCards.length} cards +
+ +
+
+ ); +} diff --git a/src/components/CardField.tsx b/src/components/CardField.tsx new file mode 100644 index 0000000..572114a --- /dev/null +++ b/src/components/CardField.tsx @@ -0,0 +1,138 @@ +import { useRef, useMemo, useLayoutEffect, useState, useCallback } from 'react'; +import type { CardData } from '../types'; +import FloatingCard from './FloatingCard'; +import ConnectionLines from './ConnectionLines'; +import type { CardRect } from './ConnectionLines'; + +interface CardFieldProps { + cards: CardData[]; + focusedId: number | null; + onFocus: (id: number | null) => void; +} + +interface LayoutPos { + x: number; + y: number; + angle: number; +} + +function computePositions(count: number, w: number, h: number): LayoutPos[] { + if (count === 0) return []; + + const cardW = 150; + const cardH = 210; + const cx = w / 2 - cardW / 2; + const cy = h / 2 - cardH / 2; + + if (count === 1) return [{ x: cx, y: cy, angle: 0 }]; + + const maxR = Math.min(w, h) * 0.35; + const minR = maxR * 0.3; + + const positions: LayoutPos[] = []; + for (let i = 0; i < count; i++) { + const t = i / count; + const angle = t * Math.PI * 2 - Math.PI / 2; + const radius = minR + (maxR - minR) * (0.5 + 0.5 * Math.sin(t * Math.PI)); + const x = cx + radius * Math.cos(angle); + const y = cy + radius * Math.sin(angle); + const rot = ((i * 137.5) % 12) - 6; + positions.push({ x, y, angle: rot }); + } + return positions; +} + +export default function CardField({ cards, focusedId, onFocus }: CardFieldProps) { + const fieldRef = useRef(null); + const cardRectsRef = useRef([]); + const [size, setSize] = useState({ w: 800, h: 500 }); + const [rects, setRects] = useState([]); + + useLayoutEffect(() => { + const el = fieldRef.current; + if (!el) return; + const ro = new ResizeObserver((entries) => { + for (const entry of entries) { + const { width, height } = entry.contentRect; + setSize({ w: width, h: height }); + } + }); + ro.observe(el); + const rect = el.getBoundingClientRect(); + setSize({ w: rect.width, h: rect.height }); + return () => ro.disconnect(); + }, []); + + const measure = useCallback(() => { + const el = fieldRef.current; + if (!el) return; + const fieldRect = el.getBoundingClientRect(); + const items = el.querySelectorAll('[data-card-id]'); + const measured: CardRect[] = []; + items.forEach((item) => { + const id = Number(item.dataset.cardId); + if (isNaN(id)) return; + const r = item.getBoundingClientRect(); + measured.push({ + id, + cx: r.left - fieldRect.left + r.width / 2, + cy: r.top - fieldRect.top + r.height / 2, + }); + }); + cardRectsRef.current = measured; + setRects(measured); + }, []); + + useLayoutEffect(() => { + if (cards.length === 0) { + setRects([]); + return; + } + const raf = requestAnimationFrame(measure); + return () => cancelAnimationFrame(raf); + }, [cards.length, measure]); + + useLayoutEffect(() => { + window.addEventListener('resize', measure); + return () => window.removeEventListener('resize', measure); + }, [measure]); + + const positions = useMemo( + () => computePositions(cards.length, size.w, size.h), + [cards.length, size.w, size.h], + ); + + const hasFocus = focusedId !== null; + + return ( +
+ {cards.length === 0 && ( +
+ select icons to reveal cards +
+ )} + + + + {cards.map((card, i) => { + const pos = positions[i] ?? { x: 0, y: 0, angle: 0 }; + const isFocused = focusedId === card.id; + return ( + { + if (el) el.dataset.cardId = String(card.id); + }} + /> + ); + })} +
+ ); +} diff --git a/src/components/CardFocusOverlay.tsx b/src/components/CardFocusOverlay.tsx new file mode 100644 index 0000000..c3ff16b --- /dev/null +++ b/src/components/CardFocusOverlay.tsx @@ -0,0 +1,77 @@ +import { motion } from 'framer-motion'; +import { getCardImagePath } from '../data/cardImages'; +import { getIconPath } from '../data/iconPaths'; +import { AE_LABELS, MFL_LABELS, SDG_LABELS } from '../data/labels'; +import type { CardData } from '../types'; + +function IconTags({ values, set, labels }: { values: number[]; set: string; labels: Record }) { + return ( +
+ {values.map((v) => ( + + + {set.toUpperCase()} {v}: {labels[v]} + + ))} +
+ ); +} + +interface CardFocusOverlayProps { + card: CardData; + onClose: () => void; +} + +export default function CardFocusOverlay({ card, onClose }: CardFocusOverlayProps) { + const imgSrc = getCardImagePath(card.id); + + return ( + +
e.stopPropagation()}> + + {card.name} + + + +

{card.name}

+ +
+

Action

+

{card.action_text}

+
+ +
+

Description

+

{card.description}

+
+ +
+

Associated Values

+
+ + + +
+
+
+
+
+ ); +} diff --git a/src/components/ConnectionLines.tsx b/src/components/ConnectionLines.tsx new file mode 100644 index 0000000..1ba8cdc --- /dev/null +++ b/src/components/ConnectionLines.tsx @@ -0,0 +1,80 @@ +import type { CardData, ValueSet } from '../types'; +import { VALUE_SET_COLORS } from '../types'; + +export interface CardRect { + id: number; + cx: number; + cy: number; +} + +function getSharedSets(a: CardData, b: CardData): ValueSet[] { + const shared: ValueSet[] = []; + if (a.ae.some((v) => b.ae.includes(v))) shared.push('ae'); + if (a.mfl.some((v) => b.mfl.includes(v))) shared.push('mfl'); + if (a.sdgs.some((v) => b.sdgs.includes(v))) shared.push('sdg'); + return shared; +} + +interface ConnectionLine { + from: { x: number; y: number }; + to: { x: number; y: number }; + color: string; +} + +interface ConnectionLinesProps { + cards: CardData[]; + rects: CardRect[]; + hasFocus: boolean; + fieldW: number; + fieldH: number; +} + +export default function ConnectionLines({ cards, rects, hasFocus, fieldW, fieldH }: ConnectionLinesProps) { + if (cards.length < 2 || rects.length < 2) return null; + + const lines: ConnectionLine[] = []; + + for (let i = 0; i < cards.length; i++) { + for (let j = i + 1; j < cards.length; j++) { + const sets = getSharedSets(cards[i], cards[j]); + if (sets.length === 0) continue; + + const ri = rects.find((r) => r.id === cards[i].id); + const rj = rects.find((r) => r.id === cards[j].id); + if (!ri || !rj) continue; + + sets.forEach((set, idx) => { + const offset = (idx - (sets.length - 1) / 2) * 6; + const dx = rj.cx - ri.cx; + const dy = rj.cy - ri.cy; + const len = Math.sqrt(dx * dx + dy * dy); + if (len < 1) return; + const nx = -dy / len; + const ny = dx / len; + + lines.push({ + from: { x: ri.cx + nx * offset, y: ri.cy + ny * offset }, + to: { x: rj.cx + nx * offset, y: rj.cy + ny * offset }, + color: VALUE_SET_COLORS[set], + }); + }); + } + } + + return ( + + {lines.map((line, i) => ( + + ))} + + ); +} diff --git a/src/components/FloatingCard.tsx b/src/components/FloatingCard.tsx new file mode 100644 index 0000000..7ff399c --- /dev/null +++ b/src/components/FloatingCard.tsx @@ -0,0 +1,61 @@ +import { motion } from 'framer-motion'; +import { getCardImagePath } from '../data/cardImages'; +import type { CardData } from '../types'; + +interface FloatingCardProps { + card: CardData; + focused: boolean; + dimmed: boolean; + x: number; + y: number; + angle: number; + onFocus: (id: number | null) => void; + cardRef?: (el: HTMLDivElement | null) => void; +} + +export default function FloatingCard({ card, focused, dimmed, x, y, angle, onFocus, cardRef }: FloatingCardProps) { + const imgSrc = getCardImagePath(card.id); + + return ( + onFocus(focused ? null : card.id)} + > +
+ {card.name} +
+
+ ); +} diff --git a/src/components/IconBar.tsx b/src/components/IconBar.tsx new file mode 100644 index 0000000..7b855ab --- /dev/null +++ b/src/components/IconBar.tsx @@ -0,0 +1,29 @@ +import type { ValueSet } from '../types'; +import IconButton from './IconButton'; + +interface IconBarProps { + set: ValueSet; + count: number; + labels: Record; + selected: Set; + onToggle: (value: number) => void; +} + +export default function IconBar({ set, count, labels, selected, onToggle }: IconBarProps) { + const values = Array.from({ length: count }, (_, i) => i + 1); + + return ( +
+ {values.map((v) => ( + + ))} +
+ ); +} diff --git a/src/components/IconButton.tsx b/src/components/IconButton.tsx new file mode 100644 index 0000000..2201cf6 --- /dev/null +++ b/src/components/IconButton.tsx @@ -0,0 +1,28 @@ +import { motion } from 'framer-motion'; +import { getIconPath } from '../data/iconPaths'; +import type { ValueSet } from '../types'; + +interface IconButtonProps { + set: ValueSet; + value: number; + label: string; + selected: boolean; + onToggle: (value: number) => void; +} + +export default function IconButton({ set, value, label, selected, onToggle }: IconButtonProps) { + const src = getIconPath(set, value); + + return ( + onToggle(value)} + whileTap={{ scale: 0.9 }} + whileHover={{ scale: 1.1 }} + animate={selected ? { scale: [1, 1.15, 1], transition: { duration: 0.3 } } : { scale: 1 }} + title={`${set.toUpperCase()}${value}: ${label}`} + > + {`${set.toUpperCase()}${value}`} + + ); +} diff --git a/src/components/IconColumn.tsx b/src/components/IconColumn.tsx new file mode 100644 index 0000000..4d06379 --- /dev/null +++ b/src/components/IconColumn.tsx @@ -0,0 +1,29 @@ +import type { ValueSet } from '../types'; +import IconButton from './IconButton'; + +interface IconColumnProps { + set: ValueSet; + count: number; + labels: Record; + selected: Set; + onToggle: (value: number) => void; +} + +export default function IconColumn({ set, count, labels, selected, onToggle }: IconColumnProps) { + const values = Array.from({ length: count }, (_, i) => i + 1); + + return ( +
+ {values.map((v) => ( + + ))} +
+ ); +} diff --git a/src/data/cardImages.ts b/src/data/cardImages.ts new file mode 100644 index 0000000..8792ad5 --- /dev/null +++ b/src/data/cardImages.ts @@ -0,0 +1,29 @@ +const CARD_IMG: (string | null)[] = [ + null, + "1-Gift-of-Water-Action.png","2-Grow-Greens-Action.png","3-Fine-Dining-Compost-Dish-Action.png", + "4-Buzzing-with-Wisdom-Action.png","5-Baking-Flatbread.png","6-Mixed-Wastemonster-Action.png", + "7-Ingredient-Detective-Action.png","8-Mindful-Eating-Action.png","9-Fermentastic-Action.png", + "10 - Tree Planter.png","11 - Seed Scout.png","12 - Natural Spritz.png", + "13 - Dashboard Designer.png","14 - Onion Bodyguard.png","15 - Local Investor.png", + "16 - Poster Designer.png","17 - Change-Maker.png","18 - Waste Remover.png", + "19 - Manure Maker.png","20 - Mixed Garden.png","21 - Ash Bug Blaster.png", + "22 - Dry & Store.png","23 - Seed Distributor.png","24 - River Cleaners.png", + "25 - Garden Documenter.png","26 - Local Variety Guardian.png","27 - Nature Regenerator.png", + "28 - Biodiversity Steward.png","29 - Land Negotiator.png","30-TJD-Booster-Action.png", + "31 - Kitchen Garden.png","32 - Balanced Plate Builder.png","33 - Indigenous Tree Lover.png", + "34 - Cover Crop Protector.png","35 - Local Food Producer.png","36 - Market Price Checker.png", + "37 - Community Meeting.png","38 - Jar Farm Product Marketer.png","39 - Food Donation Hero.png", + "40 - Windbreak Warrior.png","41 - Seed Champion.png","42 - Fruit & Tree Team.png", + "43 - Manure Cycle Drawer.png","44 - Animal Hero.png","45 - Land Magic.png", + "46 - Local Chicken Lover.png","47 - Food Dryer Explorer.png","48 - Scrap-tastic Animal Feeder.png", + "49 - Leftover Chef.png","50 - Weather Tracker Checker.png","51 - Farm Record Keeper.png", + "52 - The Water Fertilizers.png","53 - Small Farm, Big Voice.png","54 - Nature Educator.png", + "55 - Teamwork Harvest.png","56 - Sky Garden.png","57-Be-the-Change-Action.png", + "58-Ancient-Wisdom-Defender-Action.png","59-Seedling-Protector-Action.png","60-Cooperative-Builder-Action.png", +]; + +export function getCardImagePath(cardId: number): string { + const filename = CARD_IMG[cardId]; + if (!filename) return ''; + return `/cards/${encodeURI(filename)}`; +} diff --git a/src/data/iconPaths.ts b/src/data/iconPaths.ts new file mode 100644 index 0000000..c667a4c --- /dev/null +++ b/src/data/iconPaths.ts @@ -0,0 +1,20 @@ +import type { ValueSet } from '../types'; + +const SET_DIR: Record = { + ae: 'AE SVG', + mfl: 'MFL', + sdg: 'SDG', +}; + +const SET_EXT: Record = { + ae: 'svg', + mfl: 'png', + sdg: 'png', +}; + +export function getIconPath(set: ValueSet, n: number): string { + if (set === 'sdg' && n === 10) return `/icons/SDG/sdg-10.png`; + const dir = SET_DIR[set]; + const ext = SET_EXT[set]; + return `/icons/${dir}/${set.toUpperCase()}-${n}.${ext}`; +} diff --git a/src/data/labels.ts b/src/data/labels.ts new file mode 100644 index 0000000..c723e90 --- /dev/null +++ b/src/data/labels.ts @@ -0,0 +1,50 @@ +export const AE_LABELS: Record = { + 1: 'Recycling', + 2: 'Input reduction', + 3: 'Soil health', + 4: 'Animal health', + 5: 'Biodiversity', + 6: 'Synergy', + 7: 'Economic diversification', + 8: 'Co-creation of knowledge', + 9: 'Social values & diets', + 10: 'Fairness', + 11: 'Connectivity', + 12: 'Land & natural governance', + 13: 'Participation', +}; + +export const MFL_LABELS: Record = { + 1: 'Conservation', + 2: 'Production', + 3: 'Consumption', + 4: 'Trees', + 5: 'Water', + 6: 'Seeds', + 7: 'Ecosystems', + 8: 'Markets', + 9: 'Waste', + 10: 'Data', + 11: 'Governance', + 12: 'Education', +}; + +export const SDG_LABELS: Record = { + 1: 'No poverty', + 2: 'Zero hunger', + 3: 'Good health', + 4: 'Quality education', + 5: 'Gender equality', + 6: 'Clean water', + 7: 'Clean energy', + 8: 'Decent work', + 9: 'Industry & innovation', + 10: 'Reduced inequalities', + 11: 'Sustainable cities', + 12: 'Responsible consumption', + 13: 'Climate action', + 14: 'Life below water', + 15: 'Life on land', + 16: 'Peace & justice', + 17: 'Partnerships', +}; diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..d9736ad --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,9 @@ +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import App from './App'; + +createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/src/styles/app.css b/src/styles/app.css new file mode 100644 index 0000000..e0367b0 --- /dev/null +++ b/src/styles/app.css @@ -0,0 +1,373 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:root { + --bg: #070d07; + --surface: #0f1a0f; + --surface2: #162416; + --accent-ae: #4CAF50; + --accent-mfl: #009688; + --accent-sdg: #FFC107; + --text: #d0e8d0; + --text-dim: #6a8f6a; + --card-w: 150px; + --card-h: 210px; +} + +html, body, #root { + width: 100%; + height: 100%; + overflow: hidden; + background: var(--bg); + color: var(--text); + font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; +} + +.app { + width: 100vw; + height: 100vh; + display: grid; + grid-template-columns: 1fr auto; + grid-template-rows: auto 1fr auto; + grid-template-areas: + "ae mfl" + "mid mfl" + "sdg mfl"; + overflow: hidden; +} + +/* ── Icon bars ── */ + +.ae-banner { + grid-area: ae; + background: var(--surface); + border-bottom: 1px solid var(--surface2); + padding: 6px 8px; + overflow-x: auto; + overflow-y: hidden; + display: flex; + align-items: center; +} + +.sdg-banner { + grid-area: sdg; + background: var(--surface); + border-top: 1px solid var(--surface2); + padding: 6px 8px; + overflow-x: auto; + overflow-y: hidden; + display: flex; + align-items: center; +} + +.icon-bar { + display: flex; + gap: 6px; + align-items: center; +} + +/* ── Icon buttons ── */ + +.icon-btn { + background: transparent; + border: 2px solid transparent; + border-radius: 8px; + padding: 3px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: border-color 0.2s, box-shadow 0.2s; + flex-shrink: 0; +} + +.icon-btn-img { + width: 44px; + height: 44px; + object-fit: contain; + pointer-events: none; +} + +.icon-btn.ae.selected { + border-color: var(--accent-ae); + box-shadow: 0 0 12px rgba(76, 175, 80, 0.5); + background: rgba(76, 175, 80, 0.1); +} + +.icon-btn.mfl.selected { + border-color: var(--accent-mfl); + box-shadow: 0 0 12px rgba(0, 150, 136, 0.5); + background: rgba(0, 150, 136, 0.1); +} + +.icon-btn.sdg.selected { + border-color: var(--accent-sdg); + box-shadow: 0 0 12px rgba(255, 193, 7, 0.5); + background: rgba(255, 193, 7, 0.1); +} + +/* ── MFL sidebar ── */ + +.mfl-sidebar { + grid-area: mfl; + background: var(--surface); + border-left: 1px solid var(--surface2); + padding: 8px 6px; + overflow-y: auto; + overflow-x: hidden; + display: flex; + align-items: center; +} + +.icon-column { + display: flex; + flex-direction: column; + gap: 4px; + align-items: center; +} + +/* ── Center field ── */ + +.center-field { + grid-area: mid; + position: relative; + overflow: hidden; + background: radial-gradient(ellipse at center, #0f1f0f 0%, var(--bg) 70%); +} + +.field-label-top { + position: absolute; + top: 8px; + left: 50%; + transform: translateX(-50%); + display: flex; + gap: 16px; + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 2px; + color: var(--text-dim); + z-index: 5; + pointer-events: none; +} + +.field-label-top .label-ae { color: var(--accent-ae); } +.field-label-top .label-mfl { color: var(--accent-mfl); } +.field-label-top .label-sdg { color: var(--accent-sdg); } +.field-label-top .label-count { color: var(--text); } + +/* ── Card field ── */ + +.card-field { + position: relative; + width: 100%; + height: 100%; +} + +.empty-cosmos { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-dim); + font-size: 1.1rem; + letter-spacing: 3px; + text-transform: uppercase; + opacity: 0.5; +} + +/* ── Floating cards ── */ + +.floating-card { + position: absolute; + width: var(--card-w); + cursor: pointer; + transform-origin: center center; + will-change: transform; +} + +.floating-card.focused { + position: fixed; + width: var(--card-w); +} + +.floating-card-inner { + width: 100%; + aspect-ratio: 150 / 210; + border-radius: 10px; + overflow: hidden; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); +} + +.floating-card.focused .floating-card-inner { + box-shadow: 0 0 50px rgba(76, 175, 80, 0.35), 0 0 100px rgba(76, 175, 80, 0.1); +} + +.floating-card-img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +/* ── Card focus overlay ── */ + +.focus-overlay { + position: fixed; + inset: 0; + z-index: 200; + background: rgba(0, 0, 0, 0.75); + display: flex; + align-items: center; + justify-content: center; +} + +.focus-layout { + display: flex; + align-items: center; + gap: 48px; + max-width: 90vw; + max-height: 90vh; +} + +.focus-card { + flex-shrink: 0; +} + +.focus-card-img { + width: 300px; + height: 420px; + object-fit: cover; + border-radius: 12px; + box-shadow: 0 0 60px rgba(76, 175, 80, 0.3), 0 8px 40px rgba(0, 0, 0, 0.6); + display: block; +} + +.focus-details { + flex: 1; + min-width: 0; + max-width: 480px; + max-height: 80vh; + overflow-y: auto; + padding: 24px; + border-radius: 12px; + background: rgba(15, 26, 15, 0.92); + border: 1px solid var(--surface2); + box-shadow: 0 8px 40px rgba(0, 0, 0, 0.5); +} + +.focus-details h2 { + font-size: 1.6rem; + color: var(--accent-ae); + margin-bottom: 20px; + border-bottom: 1px solid var(--surface2); + padding-bottom: 12px; +} + +.detail-section { + margin-bottom: 20px; +} + +.detail-section h3 { + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 2px; + color: var(--text-dim); + margin-bottom: 6px; +} + +.focus-details .card-action { + font-size: 0.95rem; + color: var(--text); + line-height: 1.5; +} + +.focus-details .card-desc { + font-size: 0.88rem; + color: var(--text-dim); + line-height: 1.5; + font-style: italic; +} + +/* ── Icon tags in details ── */ + +.value-tags { + display: flex; + flex-direction: column; + gap: 8px; +} + +.icon-tags { + display: flex; + flex-wrap: wrap; + gap: 5px; +} + +.icon-tag { + display: inline-flex; + align-items: center; + gap: 4px; + font-size: 0.75rem; + padding: 4px 10px 4px 5px; + border-radius: 5px; + background: rgba(255, 255, 255, 0.04); + border: 1px solid rgba(255, 255, 255, 0.08); +} + +.icon-tag-img { + width: 20px; + height: 20px; + object-fit: contain; +} + +.icon-tag.ae { border-color: rgba(76, 175, 80, 0.3); color: var(--accent-ae); } +.icon-tag.mfl { border-color: rgba(0, 150, 136, 0.3); color: var(--accent-mfl); } +.icon-tag.sdg { border-color: rgba(255, 193, 7, 0.3); color: var(--accent-sdg); } + +/* ── Connection lines ── */ + +.connection-lines { + position: absolute; + top: 0; + left: 0; + pointer-events: none; + z-index: 0; +} + +/* ── Scrollbar styling ── */ + +.ae-banner::-webkit-scrollbar, +.sdg-banner::-webkit-scrollbar, +.mfl-sidebar::-webkit-scrollbar { + height: 4px; + width: 4px; +} + +.ae-banner::-webkit-scrollbar-track, +.sdg-banner::-webkit-scrollbar-track, +.mfl-sidebar::-webkit-scrollbar-track { + background: transparent; +} + +.ae-banner::-webkit-scrollbar-thumb, +.sdg-banner::-webkit-scrollbar-thumb, +.mfl-sidebar::-webkit-scrollbar-thumb { + background: var(--surface2); + border-radius: 2px; +} + +.focus-details::-webkit-scrollbar { + width: 4px; +} + +.focus-details::-webkit-scrollbar-track { + background: transparent; +} + +.focus-details::-webkit-scrollbar-thumb { + background: var(--surface2); + border-radius: 2px; +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..c1a524b --- /dev/null +++ b/src/types.ts @@ -0,0 +1,24 @@ +export interface CardData { + id: number; + name: string; + action_text: string; + description: string; + ae: number[]; + mfl: number[]; + sdgs: number[]; +} + +export type ValueSet = 'ae' | 'mfl' | 'sdg'; + +export interface ConnectionLine { + from: { x: number; y: number }; + to: { x: number; y: number }; + color: string; + label: string; +} + +export const VALUE_SET_COLORS: Record = { + ae: '#4CAF50', + mfl: '#009688', + sdg: '#FFC107', +}; diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..0581885 --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..3dd4001 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "strict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..9ffcc67 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], +})