diff --git a/package-lock.json b/package-lock.json index 8cb45b3..fe8608c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "class-variance-authority": "0.7.1", "clsx": "2.1.1", "lucide-react": "^0.577.0", + "marked": "^18.0.2", "motion": "^12.23.24", "react": "^19.2.4", "react-dom": "^19.2.4", @@ -22,6 +23,8 @@ "tailwind-merge": "^3.5.0" }, "devDependencies": { + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", "autoprefixer": "^10.4.27", "gh-pages": "^6.3.0", "tailwindcss": "^4.2.2", @@ -1429,6 +1432,26 @@ "undici-types": "~6.21.0" } }, + "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==", + "devOptional": true, + "license": "MIT", + "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==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, "node_modules/@vitejs/plugin-react": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", @@ -1640,6 +1663,13 @@ "url": "https://opencollective.com/express" } }, + "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==", + "devOptional": true, + "license": "MIT" + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -2358,6 +2388,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/marked": { + "version": "18.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.2.tgz", + "integrity": "sha512-NsmlUYBS/Zg57rgDWMYdnre6OTj4e+qq/JS2ot3KrYLSoHLw+sDu0Nm1ZGpRgYAq6c+b1ekaY5NzVchMCQnzcg==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", diff --git a/package.json b/package.json index 7ff98fb..a907804 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "class-variance-authority": "0.7.1", "clsx": "2.1.1", "lucide-react": "^0.577.0", + "marked": "^18.0.2", "motion": "^12.23.24", "react": "^19.2.4", "react-dom": "^19.2.4", @@ -28,6 +29,8 @@ "tailwind-merge": "^3.5.0" }, "devDependencies": { + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", "autoprefixer": "^10.4.27", "gh-pages": "^6.3.0", "tailwindcss": "^4.2.2", diff --git a/src/App.tsx b/src/App.tsx index fb6c0aa..afd4c76 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,6 +3,8 @@ import Index from "./pages/Index"; import Privacy from "./pages/Privacy"; import NotFound from "./pages/NotFound"; import Waitlist from "./pages/Waitlist"; +import BlogList from "./pages/BlogList"; +import BlogPost from "./pages/BlogPost"; import CookieConsent from "./components/ui/CookieConsent"; const App = () => ( @@ -12,6 +14,8 @@ const App = () => ( } /> } /> } /> + } /> + } /> {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} } /> diff --git a/src/assets/blog-images/afra-ramio-cp2HCVzguw4-unsplash.jpg b/src/assets/blog-images/afra-ramio-cp2HCVzguw4-unsplash.jpg new file mode 100644 index 0000000..0a06ac4 Binary files /dev/null and b/src/assets/blog-images/afra-ramio-cp2HCVzguw4-unsplash.jpg differ diff --git a/src/assets/blog-images/dagmar-klauzova-afBc-k2luH4-unsplash.jpg b/src/assets/blog-images/dagmar-klauzova-afBc-k2luH4-unsplash.jpg new file mode 100644 index 0000000..efd3c89 Binary files /dev/null and b/src/assets/blog-images/dagmar-klauzova-afBc-k2luH4-unsplash.jpg differ diff --git a/src/assets/blog-images/eric-ward-ISg37AI2A-s-unsplash.jpg b/src/assets/blog-images/eric-ward-ISg37AI2A-s-unsplash.jpg new file mode 100644 index 0000000..3df44a8 Binary files /dev/null and b/src/assets/blog-images/eric-ward-ISg37AI2A-s-unsplash.jpg differ diff --git a/src/assets/blog-images/jamie-street-m4BkRRprvP0-unsplash.jpg b/src/assets/blog-images/jamie-street-m4BkRRprvP0-unsplash.jpg new file mode 100644 index 0000000..df34267 Binary files /dev/null and b/src/assets/blog-images/jamie-street-m4BkRRprvP0-unsplash.jpg differ diff --git a/src/assets/blog-images/joe-caione-qO-PIF84Vxg-unsplash.jpg b/src/assets/blog-images/joe-caione-qO-PIF84Vxg-unsplash.jpg new file mode 100644 index 0000000..b820c72 Binary files /dev/null and b/src/assets/blog-images/joe-caione-qO-PIF84Vxg-unsplash.jpg differ diff --git a/src/assets/blog-images/rafaella-waasdorp-EkzMdwI_YE4-unsplash.jpg b/src/assets/blog-images/rafaella-waasdorp-EkzMdwI_YE4-unsplash.jpg new file mode 100644 index 0000000..306fb42 Binary files /dev/null and b/src/assets/blog-images/rafaella-waasdorp-EkzMdwI_YE4-unsplash.jpg differ diff --git a/src/assets/blog-images/tatum-bergen-Vt3odrTM4cQ-unsplash.jpg b/src/assets/blog-images/tatum-bergen-Vt3odrTM4cQ-unsplash.jpg new file mode 100644 index 0000000..a96e735 Binary files /dev/null and b/src/assets/blog-images/tatum-bergen-Vt3odrTM4cQ-unsplash.jpg differ diff --git a/src/assets/blog-images/troy-bridges-lbRNtUZPXXQ-unsplash.jpg b/src/assets/blog-images/troy-bridges-lbRNtUZPXXQ-unsplash.jpg new file mode 100644 index 0000000..fa1ee2d Binary files /dev/null and b/src/assets/blog-images/troy-bridges-lbRNtUZPXXQ-unsplash.jpg differ diff --git a/src/assets/raw-blogs/Best Off-Leash Areas for Dogs in Lagos (and Nearby).md b/src/assets/raw-blogs/Best Off-Leash Areas for Dogs in Lagos (and Nearby).md new file mode 100644 index 0000000..32a54da --- /dev/null +++ b/src/assets/raw-blogs/Best Off-Leash Areas for Dogs in Lagos (and Nearby).md @@ -0,0 +1,136 @@ +--- +title: "Best Off-Leash Areas for Dogs in Lagos (and Nearby)" +date: 2025-05-01 +description: "Looking for off-leash areas for dogs in Lagos, Portugal? Discover the best spots for safe, enjoyable off-leash walks, plus what to watch out for." +category: Insights +readTime: "6 min" +live: true +--- + +### **Introduction** + +Finding a good place to let your dog off-leash in Lagos isn’t always straightforward. + +Some areas are: + +* Too busy +* Too unpredictable +* Or not actually suitable for your dog + +But when you find the right spot, it changes everything. + +Here are some of the best off-leash-friendly areas around Lagos, and how to choose the right one for your dog. + +--- + +## **1\. Mata Nacional de Barão de São João** + +**Best for:** Most dogs + **Why it works:** + +* Wide, open forest trails +* Plenty of space to create distance +* Shaded and cooler + +Ideal for dogs who need **space without isolation** + +--- + +## **2\. Praia da Bordeira (West Coast)** + +**Best for:** High-energy dogs + **Why it works:** + +* Huge open beach +* Low crowd density (outside peak times) +* Great for running + + Watch: + +* Wind exposure +* Seasonal rules + +--- + +## **3\. Ria de Alvor (Quiet Sections)** + +**Best for:** Lower-energy or calm dogs + **Why it works:** + +* Flat terrain +* Open visibility + +Avoid peak hours, it can get busy + +--- + +## **4\. Monchique Countryside** + +**Best for:** Dogs needing low stimulation + **Why it works:** + +* Quiet +* Natural +* Cooler temperatures + +Great for reactive or sensitive dogs + +--- + +## **5\. Local Countryside Trails (Hidden Gems)** + +Often overlooked, but some of the best options. + +These areas offer: + +* Lower dog traffic +* More control +* Flexible space + +Especially useful for: + +* Training recall +* Building confidence + + +Check out trails near countryside villages like Bensafrim and Vila do Bispo. + +--- + +## **Important: Not Every “Off-Leash Area” Is Right** + +Just because a place allows off-leash doesn’t mean it’s suitable. + +Before letting your dog off: + +* Check the environment +* Look at dog traffic +* Consider your dog’s behaviour + +--- + +## **What Makes a Good Off-Leash Spot?** + +The best places have: + +* Space to move freely +* Clear visibility +* Low unpredictability +* An environment suited to your dog + +--- + +## **Final Thoughts** + +The goal isn’t just to let your dog off-leash. + +It’s to do it in a way that: + +* Feels safe +* Feels calm +* And actually benefits your dog + +Because the right environment makes all the difference. + + + diff --git a/src/assets/raw-blogs/How to Choose the Right Walk for Your Dog (Based on Their Personality and Needs).md b/src/assets/raw-blogs/How to Choose the Right Walk for Your Dog (Based on Their Personality and Needs).md new file mode 100644 index 0000000..f90308c --- /dev/null +++ b/src/assets/raw-blogs/How to Choose the Right Walk for Your Dog (Based on Their Personality and Needs).md @@ -0,0 +1,141 @@ +--- +title: "How to Choose the Right Walk for Your Dog (Based on Their Personality and Needs)" +date: 2025-04-20 +description: "Not all walks are equal. Learn how to choose the right environment for your dog based on energy, temperament, and needs." +category: Strategy +readTime: "5 min" +live: true +--- + + +### **Introduction** + +Choosing a walk for your dog often comes down to convenience. + +What’s nearby. +What you’ve done before. +What seems “good enough.” + +But the best walks aren’t chosen that way. + +They’re chosen based on **your dog**. + +--- + +### **Step 1: Understand Your Dog’s Profile** + +Start with the basics: + +* Energy level (low, medium, high) +* Sociability (loves dogs vs prefers space) +* Confidence (bold vs cautious) +* Sensitivities (noise, movement, crowds) +* Physical needs (age, injuries, endurance) + +This becomes your baseline. + +--- + +### **Step 2: Match the Environment** + +Now consider the walk itself: + +#### **High-energy dogs** + +Need: + +* Long routes +* Open space +* Opportunities to run + +Best environments: + +* Wide trails +* Open countryside +* Low restriction areas + +--- + +**Sensitive or reactive dogs** + +Need: + +* Predictability +* Space from others +* Low stimulation + +Best environments: + +* Quiet trails +* Low dog traffic areas +* Wide paths with visibility + +--- + +#### **Older or recovering dogs** + +Need: + +* Shorter distances +* Even terrain +* Shade + +Best environments: + +* Flat trails +* Forest paths +* Low-exertion routes + +--- + +### **Step 3: Look Beyond the Surface** + +A trail might look perfect, but details matter: + +* Is it narrow or open? +* Is there shade? +* Are dogs typically off-leash there? +* Is it busy at certain times? + +These small factors make a big difference. + +--- + +### **Step 4: Pay Attention to Feedback** + +Your dog will tell you if it’s working. + +Look for: + +* Relaxed body language +* Natural curiosity +* Steady energy (not frantic or shut down) +* Willing engagement with surroundings + +If you’re seeing stress signals, it may not be the right fit. + +--- + +### **Step 5: Prioritise Variety Intentionally** + +Variety is important. + +But random variety isn’t helpful. + +Instead: + +* Rotate between environments that suit your dog +* Gradually introduce new challenges +* Build confidence over time + +--- + +### **Final Thoughts** + +The best walk isn’t the longest or the most popular. + +It’s the one that fits your dog. + +Because when you choose the right environment, you’re not just going for a walk, you’re creating a better experience for both of you. + + diff --git a/src/assets/raw-blogs/How to Give a Reactive Dog a Fulfilling Life (Without Avoiding the World).md b/src/assets/raw-blogs/How to Give a Reactive Dog a Fulfilling Life (Without Avoiding the World).md new file mode 100644 index 0000000..1888449 --- /dev/null +++ b/src/assets/raw-blogs/How to Give a Reactive Dog a Fulfilling Life (Without Avoiding the World).md @@ -0,0 +1,130 @@ +--- +title: "How to Give a Reactive Dog a Fulfilling Life (Without Avoiding the World)" +date: 2025-03-15 +description: "Have a reactive dog? Learn how to give them a fulfilling life with the right environments, structure, and approach." +category: Insights +readTime: "7 min" +live: true +--- + +### **Introduction** + +Living with a reactive dog can feel limiting. + +You might: + +* Avoid certain places +* Walk at odd hours +* Stick to the same safe routes + +But here’s the truth: + +Your dog can still have a full, enriching life. + +It just requires a different approach. + +--- + +## **What Reactivity Often Means** + +Reactivity isn’t “bad behaviour.” + +It’s usually: + +* Overstimulation +* Fear or uncertainty +* Lack of space +* Too much, too quickly + +--- + +## **Environment Matters More Than Training Alone** + +Training is important, but environment is foundational. + +Even a well-trained dog can struggle in: + +* Busy areas +* Narrow paths +* High dog-traffic zones + +--- + +## **Choosing the Right Environments** + +Look for places that offer: + +* Space to move away +* Predictability +* Lower levels of stimulation + +Examples: + +* Wide trails +* Quiet countryside +* Off-peak walking times + +--- + +## **Give Them the Right Kind of Freedom** + +Reactive dogs don’t need: + +* More pressure +* More exposure too quickly + +They need: + +* Controlled experiences +* Space to decompress +* Opportunities to succeed + +--- + +## **Common Mistakes** + +* Taking them to busy “dog-friendly” areas +* Forcing social interactions +* Ignoring early stress signals + +--- + +## **Building Confidence Over Time** + +Start small. + +* Familiar environments +* Gradual changes +* Positive reinforcement + +Confidence grows through: + +Consistent, successful experiences + +--- + +## **Fulfilment Looks Different** + +A fulfilling life doesn’t mean: + +* Busy parks +* Social environments + +It means: + +* Calm exploration +* Comfortable movement +* Feeling safe in the world + +--- + +## **Final Thoughts** + +Your dog doesn’t need to handle everything. + +They just need the **right environments to thrive in**. + +And when you give them that, everything starts to shift. + + + diff --git a/src/assets/raw-blogs/Off-Leash Dog Rules in Portugal_ What You Need to Know in 2026.md b/src/assets/raw-blogs/Off-Leash Dog Rules in Portugal_ What You Need to Know in 2026.md new file mode 100644 index 0000000..621d129 --- /dev/null +++ b/src/assets/raw-blogs/Off-Leash Dog Rules in Portugal_ What You Need to Know in 2026.md @@ -0,0 +1,134 @@ +--- +title: "Off-Leash Dog Rules in Portugal: What You Need to Know in 2026" +date: 2026-04-01 +description: "Can you walk your dog off-leash in Portugal? Learn the rules, risks, and best practices for safe and legal off-leash walks." +category: Trends +readTime: "6 min" +live: true +--- + +### **Introduction** + +Letting your dog off-leash is one of the simplest ways to give them freedom, enrichment, and a more natural experience. + +But if you’re living in or visiting Portugal, the rules aren’t always obvious, and getting it wrong can lead to fines, unsafe situations, or conflict with others. + +Here’s what you need to know. + +--- + +### **Are Dogs Allowed Off-Leash in Portugal?** + +In general, dogs in Portugal are expected to be **under control at all times in public spaces**. + +That doesn’t always mean physically on a leash \- but it *does* mean: + +* Your dog must respond reliably to commands +* They must not pose a risk to people, other animals, or property +* They must not cause disturbance + +In many **urban areas**, leash use is effectively required. +In more **rural or remote areas**, off-leash walking is more commonly accepted \- but still comes with responsibility. + +--- + +### **Official Rules, Restrictions, and Fines** + +In Portugal, there are also specific legal restrictions to be aware of, especially around beaches and public spaces. During the summer season (typically June to September), many beaches prohibit dogs entirely, regardless of whether they are on or off-leash. These rules are set by local municipalities and are usually clearly signposted at beach entrances. Outside of summer, some beaches become dog-friendly again, but leash expectations can still apply. + +Failing to follow these rules can result in on-the-spot fines, which typically range from around €25 up to €250 or more, depending on the situation and location. Additional penalties may apply if a dog is considered out of control, causes disturbance, or poses a risk to others. + +Because enforcement and rules can vary by region, it’s always worth checking local signage before letting your dog off-leash, especially in popular or seasonal areas. + +--- + +### **Where Off-Leash Walking Is (Usually) Acceptable** + +You’re more likely to safely and responsibly walk your dog off-leash in: + +* Quiet countryside trails +* Forest paths with low foot traffic +* Remote coastal routes (away from crowds) +* Open land where visibility is high + +However, even in these areas, it’s not just about permission \- it’s about suitability for your dog. + +--- + +### **Where You Should Keep Your Dog On-Leash** + +There are several environments where off-leash walking is **not recommended or restricted**: + +* Busy beaches (especially during peak season) +* Urban parks and promenades +* Narrow cliff paths +* Areas with livestock or wildlife +* High dog-traffic zones + +Even if technically allowed, these environments can be: + +* Overstimulating +* Unsafe +* Stressful for reactive or sensitive dogs + +--- + +### **The Risks Most Dog Owners Overlook** + +Many owners assume off-leash \= freedom. + +But without the right environment, it can quickly become the opposite. + +Common risks include: + +* Uncontrolled interactions with other dogs +* Encounters with wildlife or livestock +* Sudden terrain hazards (cliffs, unstable ground) +* Overheating in exposed areas +* Poor recall in unfamiliar settings + +The result? Stress for both you *and* your dog. + +--- + +### **Why “Dog-Friendly” Isn’t Enough** + +A beach might allow dogs. +A trail might be popular with dog walkers. + +But that doesn’t mean it’s **right for your dog**. + +Every dog has different needs: + +* Some thrive in social, high-energy spaces +* Others need quiet, predictable environments +* Some need open space to run +* Others need structured, low-stimulation walks + +Choosing the wrong environment can undo the very thing you’re trying to give them. + +--- + +### **How to Choose the Right Off-Leash Environment** + +Before letting your dog off-leash, ask: + +* How busy is this area? +* Are there other dogs nearby? +* Is the terrain safe and visible? +* Is there wildlife or livestock? +* Will my dog feel confident here, or overwhelmed? + +The goal isn’t just freedom. +It’s **setting your dog up for a good time**. + +--- + +### **Final Thoughts** + +Walking your dog off-leash in Portugal can be an awesome experience, when it’s done in the right place, at the right time, for the right dog. + +Because it’s not just about whether dogs are allowed. +It’s about whether the environment actually fits. + + diff --git a/src/assets/raw-blogs/The Importance of Variety in Your Dog’s Life (And How to Do It Right).md b/src/assets/raw-blogs/The Importance of Variety in Your Dog’s Life (And How to Do It Right).md new file mode 100644 index 0000000..277033c --- /dev/null +++ b/src/assets/raw-blogs/The Importance of Variety in Your Dog’s Life (And How to Do It Right).md @@ -0,0 +1,125 @@ +--- +title: "The Importance of Variety in Your Dog’s Life (And How to Do It Right)" +date: 2025-02-28 +description: "Dogs need more than the same daily walk. Learn why variety matters and how to safely introduce new environments into your dog’s routine." +category: Strategy +readTime: "5 min" +live: true +--- + +### **Introduction** + +Most dogs live very predictable lives. + +The same route. +The same pace. +The same environment. + +And while routine has its place, too much of it can limit your dog’s experience of the world. + +--- + +## **Why Variety Matters** + +Dogs are naturally curious. + +They’re wired to: + +* Explore +* Smell +* Navigate new environments + +When every walk is the same, they miss out on: + +* Mental stimulation +* Physical engagement +* Confidence-building experiences + +--- + +## **It’s Not Just About Exercise** + +A longer walk doesn’t always mean a better walk. + +A short walk in a new environment can be: + +* More stimulating +* More engaging +* More fulfilling + +--- + +## **The Benefits of Variety** + +Introducing new environments can: + +* Reduce boredom +* Improve behaviour +* Build confidence +* Strengthen your bond + +--- + +## **But Random Variety Isn’t the Answer** + +This is where many owners get it wrong. + +Throwing your dog into: + +* Busy environments +* Unpredictable situations +* Overstimulating spaces + +Can actually: + +* Increase stress +* Lead to reactivity +* Create negative experiences + +--- + +## **How to Introduce Variety Properly** + +### **1\. Start with similar environments** + +If your dog is used to quiet trails, try new *quiet* trails first. + +--- + +### **2\. Change one variable at a time** + +* New location, same energy level +* New terrain, same level of stimulation + +--- + +### **3\. Watch your dog’s response** + +Let their behaviour guide your decisions. + +--- + +### **4\. Build gradually** + +Confidence grows with positive experiences. + +--- + +## **The Goal** + +Not constant novelty. + +But **intentional variation** that suits your dog. + +--- + +## **Final Thoughts** + +Your dog doesn’t need more walks. + +They need **better, more varied experiences**. + +Because when you get it right, even a simple walk can feel completely different. + + + diff --git a/src/assets/raw-blogs/Top 10 Hikes to do with your Dog in Lagos and the Algarve.md b/src/assets/raw-blogs/Top 10 Hikes to do with your Dog in Lagos and the Algarve.md new file mode 100644 index 0000000..99c5f4e --- /dev/null +++ b/src/assets/raw-blogs/Top 10 Hikes to do with your Dog in Lagos and the Algarve.md @@ -0,0 +1,159 @@ +--- +title: "Top 10 Hikes to do with your Dog in Lagos and the Algarve" +date: 2025-06-10 +description: "Discover the best hikes in Lagos and the Algarve to take your dog on. From coastal trails to quiet forests, find the right walk for your dog." +category: Insights +readTime: "8 min" +live: true +--- + +### **Introduction** + +If you’re based in or visiting Lagos, you’re surrounded by some of the most beautiful walking routes in Europe. + +But not all hikes are equal \- especially when you’re walking with a dog. + +Some are: + +* Too exposed +* Too busy +* Too steep +* Or just not suited to your dog’s needs + +Here are 10 of the best dog-friendly hikes in the Algarve, plus what makes each one a good fit. + +--- + +## **1\. Ponta da Piedade Coastal Walk** + +**Best for:** Calm, confident dogs + **Type:** Cliffside / scenic + +Incredible views, but: + +* Narrow in parts +* High foot traffic + +Best early morning or off-season + +--- + +## **2\. Mata Nacional de Barão de São João** + +**Best for:** Most dogs + **Type:** Forest / shaded + +* Wide trails +* Plenty of shade +* Lower stimulation + +One of the most versatile options near Lagos + +--- + +## **3\. Ria de Alvor Nature Reserve** + +**Best for:** Low to moderate energy dogs + **Type:** Boardwalk / coastal + +* Flat and accessible +* Can be busy +* Limited shade + +--- + +## **4\. Rocha da Pena (near Loulé)** + +**Best for:** High-energy, confident dogs + **Type:** Rocky / inland + +* More challenging terrain +* Great for adventurous dogs + +--- + +## **5\. Praia da Bordeira (West Coast)** + +**Best for:** High-energy dogs + **Type:** Beach / open space + +* Huge open areas +* Ideal for running +* Watch for wind \+ exposure + +--- + +## **6\. Monchique Forest Trails** + +**Best for:** Hot days / sensitive dogs + **Type:** Mountain / forest + +* Cooler temperatures +* Shaded routes +* Varied terrain + +--- + +## **7\. Lagos to Praia da Luz Walk** + +**Best for:** Moderate energy dogs + **Type:** Coastal mix + +* Combination of paths and views +* Can be busy in sections + +--- + +## **8\. Parque Natural do Sudoeste Alentejano** + +**Best for:** Dogs needing space + **Type:** Wild / remote + +* Less crowded +* More natural terrain + +--- + +## **9\. Seven Hanging Valleys Trail** + +**Best for:** Experienced dogs only + **Type:** Cliffside + +* Stunning but narrow +* Not suitable for reactive or unpredictable dogs + +--- + +## **How to Choose the Right One** + +Instead of asking: + +“Which hike is best?” + +Ask: + +“Which hike is right for my dog?” + +Consider: + +* Energy level +* Sociability +* Confidence +* Weather +* Time of day + +--- + +## **Final Thoughts** + +The Algarve is full of incredible places to explore with your dog. + +But the best walks aren’t just beautiful. + +They’re the ones that: + +* Suit your dog +* Feel comfortable +* And allow them to be at their best + + diff --git a/src/assets/raw-blogs/Walking Your Dog in the Algarve_ Hidden Dangers Every Owner Should Know.md b/src/assets/raw-blogs/Walking Your Dog in the Algarve_ Hidden Dangers Every Owner Should Know.md new file mode 100644 index 0000000..f539c63 --- /dev/null +++ b/src/assets/raw-blogs/Walking Your Dog in the Algarve_ Hidden Dangers Every Owner Should Know.md @@ -0,0 +1,201 @@ +--- +title: "Walking Your Dog in the Algarve: Hidden Dangers Every Owner Should Know" +date: 2025-07-05 +description: "Walking your dog in the Algarve? Learn about hidden dangers like pine processionary caterpillars, grass seeds, heat, and more — plus how to keep your dog safe." +category: Tutorial +readTime: "7 min" +live: true +--- + +### **Introduction** + +The Algarve is one of the best places in Europe to explore with your dog. + +Cliffside trails. +Quiet forests. +Open countryside. + +But beneath all that beauty, there are a few **less obvious risks** that many dog owners (especially newcomers) don’t see coming. + +Knowing what to look out for can make the difference between a great walk and an emergency vet visit. + +--- + +## **1\. Pine Processionary Caterpillars (High Risk)** + +These are one of the **most dangerous hazards** for dogs in Portugal. + +They appear in: + +* Pine forests +* Woodland trails +* Warmer months (typically late winter to spring) + +### **Why they’re dangerous:** + +Their tiny hairs contain a toxin that can cause: + +* Severe swelling of the mouth and tongue +* Vomiting +* Difficulty breathing +* Tissue damage (in serious cases) + +### **What to look for:** + +* Caterpillars moving in a line (procession) +* Nests in pine trees (silk-like bundles) + +### **What to do:** + +* Avoid pine-heavy areas during peak season +* Keep your dog close in these environments +* If contact happens: **go to a vet immediately** + +--- + +## **2\. Grass Seeds (Foxtails)** + +Small, sharp, and incredibly problematic. + +They’re common in: + +* Dry grasslands +* Trail edges +* Late spring and summer + +### **Why they’re dangerous:** + +They can lodge in: + +* Noses +* Ears +* Eyes +* Between toes + +And they don’t come out easily—they move deeper into the body. + +### **Signs to watch for:** + +* Sneezing repeatedly +* Licking paws +* Head shaking +* Sudden discomfort + +### **Prevention:** + +* Avoid overgrown paths +* Check your dog after walks +* Pay close attention to paws and ears + +--- + +## **3\. Heat and Sun Exposure** + +The Algarve sun is intense, especially in summer. + +Even when it doesn’t feel extreme to you, it can be too much for your dog. + +### **Risks:** + +* Overheating +* Dehydration +* Burnt paw pads + +### **Safer approach:** + +* Walk early morning or late evening +* Choose shaded routes (forests \> exposed cliffs) +* Always carry water +* Test the ground temperature before walking + +--- + +## **4\. Livestock and Wildlife** + +Many rural trails pass through areas with: + +* Wild cats +* Dog packs +* Goats +* Sheep +* Cattle +* Wildlife (including birds and small animals) + +### **Why it matters:** + +* Dogs with high prey drive may chase +* Farmers are protective of their animals +* Situations can escalate quickly + +### **Best practice:** + +* Keep your dog on-leash around animals +* Be aware of your surroundings +* Respect local land use + +--- + +## **5\. Cliffs and Unstable Terrain** + +The Algarve coastline is stunning, but not always forgiving. + +### **Risks:** + +* Loose rocks +* Narrow paths +* Sudden drops + +### **Important:** + +Even confident dogs can misjudge terrain. + +### **Safer approach:** + +* Avoid narrow cliff paths with reactive or excitable dogs +* Keep distance from edges +* Choose wider, more stable trails when possible + +--- + +## **6\. High Dog Traffic Areas** + +Some popular walking spots can be: + +* Busy +* Unpredictable +* Overstimulating + +This can be especially challenging for: + +* Reactive dogs +* Nervous dogs +* Dogs in training + +--- + +## **Why Environment Matters More Than You Think** + +Most of these risks aren’t just about location. + +They’re about **fit**. + +The same trail can be: + +* Perfect for one dog +* Completely wrong for another + +That’s why choosing where you walk isn’t just about what looks good—it’s about what actually works for your dog. + +--- + +## **Final Thoughts** + +Walking your dog in the Algarve can be one of the best parts of living or visiting here. + +But the best walks happen when you: + +* Know what to look out for +* Choose environments intentionally +* Match the walk to your dog’s needs + + diff --git a/src/assets/raw-blogs/Why “Dog-Friendly” Isn’t Enough (And What Actually Matters).md b/src/assets/raw-blogs/Why “Dog-Friendly” Isn’t Enough (And What Actually Matters).md new file mode 100644 index 0000000..89204f6 --- /dev/null +++ b/src/assets/raw-blogs/Why “Dog-Friendly” Isn’t Enough (And What Actually Matters).md @@ -0,0 +1,147 @@ +--- +title: "Why “Dog-Friendly” Isn’t Enough (And What Actually Matters)" +date: 2025-01-18 +description: "“Dog-friendly” doesn’t always mean suitable. Learn what really makes a walk right for your dog, and how to choose better environments." +category: Insights +readTime: "5 min" +live: true +--- + +### **Introduction** + +“Dog-friendly” is everywhere. + +Dog-friendly cafés. +Dog-friendly beaches. +Dog-friendly trails. + +But here’s the problem: + +**Dog-friendly doesn’t mean right for your dog.** + +And relying on that label alone is one of the mistakes dog owners make. + +--- + +### **The Problem with “Dog-Friendly”** + +The term sounds helpful, but it’s incredibly broad. + +A place can be dog-friendly and still be: + +* Overcrowded +* Loud and overstimulating +* Too narrow or restrictive +* Unsafe for certain temperaments +* Physically unsuitable for your dog + +It tells you one thing: + +Dogs are allowed. + +It tells you nothing about: + +Whether your dog will actually enjoy being there. + +--- + +### **Every Dog Experiences the World Differently** + +This is where most advice falls short. + +Dogs aren’t interchangeable. + +Some are: + +* Highly social and confident +* Sensitive to noise and movement +* Reactive around other dogs +* Driven by scent or prey +* Recovering from injury +* Low energy or ageing + +The same environment can feel: + +* Exciting for one dog +* Overwhelming for another + +--- + +### **What Actually Makes a Walk “Good”** + +A good walk isn’t about distance or popularity. + +It’s about **fit**. + +The right walk considers: + +* **Dog traffic** – busy vs quiet +* **Space** – open vs narrow +* **Terrain** – soft ground vs hard surfaces +* **Exposure** – shaded vs direct sun +* **Environment** – forest, beach, rural, urban +* **Stimulation level** – predictable vs chaotic + +When these align with your dog’s needs, everything changes. + +--- + +### **Signs You’re Choosing the Wrong Environments** + +You might notice: + +* Your dog becomes overstimulated or reactive +* They seem stressed or hyper-alert +* They pull, bark, or shut down +* Walks feel unpredictable or tense + +It’s not always a training issue. + +Often, it’s an **environment mismatch**. + +--- + +### **Why This Matters More Than Ever** + +Modern dog ownership has shifted. + +Dogs are: + +* Living in more urban environments +* Exposed to more stimulation +* Expected to behave in unnatural settings + +That means we need to be more intentional about: + +Where we take them \- not just how often + +--- + +### **A Better Way to Think About Walks** + +Instead of asking: + +“Is this place dog-friendly?” + +Ask: + +“Is this place right for my dog?” + +That shift alone can: + +* Reduce stress +* Improve behaviour +* Increase confidence +* Create better shared experiences + +--- + +### **Final Thoughts** + +Your dog doesn’t need more places. + +They need the **right places**. + +Because when the environment fits, everything else gets easier. + + diff --git a/src/components/landing/Header.tsx b/src/components/landing/Header.tsx index 6eef7b8..c5aa444 100644 --- a/src/components/landing/Header.tsx +++ b/src/components/landing/Header.tsx @@ -26,6 +26,7 @@ const Header = () => {
Features How It Works + setIsMenuOpen(false)}>Blogs Join Waitlist diff --git a/src/index.css b/src/index.css index 0580512..0125945 100644 --- a/src/index.css +++ b/src/index.css @@ -37,3 +37,31 @@ @utility bento-card { @apply bg-bg-secondary border border-border-subtle rounded-3xl p-8 transition-all duration-300; } + +/* Semantic utility aliases to map theme variables to class names used across the app */ +:root { + /* expose aliases for legacy class names */ +} + +.bg-background { background-color: var(--color-bg-primary); } +.bg-bg-primary { background-color: var(--color-bg-primary); } +.bg-bg-secondary { background-color: var(--color-bg-secondary); } +.bg-bg-variant { background-color: var(--color-bg-variant); } + +.text-foreground { color: var(--color-text-primary); } +.text-text-primary { color: var(--color-text-primary); } +.text-text-secondary { color: var(--color-text-secondary); } +.text-text-muted { color: var(--color-text-muted); } +.text-muted-foreground { color: var(--color-text-muted); } + +.border-border { border-color: var(--color-border-subtle); } +.border-border-subtle { border-color: var(--color-border-subtle); } + +.bg-brand { background-color: var(--color-brand); } +.bg-brand-light { background-color: var(--color-brand-light); } +.text-bg-primary { color: var(--color-bg-primary); } + +.bg-secondary { background-color: rgba(255,255,255,0.04); } +.text-secondary-foreground { color: var(--color-text-secondary); } + +.glass { background-color: rgba(255,255,255,0.05); } diff --git a/src/lib/blogs.ts b/src/lib/blogs.ts new file mode 100644 index 0000000..c0fda24 --- /dev/null +++ b/src/lib/blogs.ts @@ -0,0 +1,73 @@ +export type BlogPost = { + slug: string; + title: string; + date?: string; // created at / published date + description?: string; + content: string; + category?: string; + readTime?: string; + live?: boolean; +}; + +function slugify(name: string) { + return name + .toLowerCase() + .replace(/\.md$/, '') + .replace(/[^a-z0-9]+/g, '-') + .replace(/(^-|-$)/g, ''); +} + +function parseFrontmatter(raw: string) { + const fmMatch = raw.match(/^---\n([\s\S]*?)\n---/); + const fm: Record = {}; + let content = raw; + if (fmMatch) { + const body = fmMatch[1]; + body.split(/\n+/).forEach((line) => { + const idx = line.indexOf(':'); + if (idx > -1) { + const key = line.slice(0, idx).trim(); + let val = line.slice(idx + 1).trim().replace(/^"|"$/g, ''); + // coerce booleans + if (/^(true|false)$/i.test(val)) { + fm[key] = val.toLowerCase() === 'true'; + } else { + fm[key] = val; + } + } + }); + content = raw.slice(fmMatch[0].length).trim(); + } + return { fm, content }; +} + +// Import all markdown files from the assets raw-blogs folder as raw strings +const modules = import.meta.glob('../assets/raw-blogs/*.md', { eager: true, as: 'raw' }) as Record; + +const posts = Object.entries(modules).map(([path, raw]) => { + const file = path.split('/').pop()!; + const slug = slugify(file); + const { fm, content } = parseFrontmatter(raw); + return { + slug, + title: fm.title ?? file.replace(/\.md$/, ''), + date: fm.date, + description: fm.description, + content, + category: fm.category, + readTime: fm.readTime, + live: typeof fm.live === 'boolean' ? fm.live : fm.live === 'true', + } as BlogPost; +}).sort((a, b) => { + const da = a.date ?? ''; + const db = b.date ?? ''; + return db.localeCompare(da); +}); + +export function getAllPosts(): BlogPost[] { + return posts; +} + +export function getPostBySlug(slug: string): BlogPost | undefined { + return posts.find((p) => p.slug === slug); +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts index a5ef193..9c063d2 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -4,3 +4,28 @@ import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } + +/** + * Return an Unsplash Source URL for a given query and size. + * Uses the Unsplash Source endpoint so no API key is required. + */ +export function getUnsplashImage(query: string, width = 1200, height = 675, seed?: string) { + const q = encodeURIComponent(query || 'nature'); + // `sig` helps vary the image per post so images don't all cache to the same photo + const sig = seed ? `&sig=${encodeURIComponent(seed)}` : ''; + return `https://source.unsplash.com/${width}x${height}/?${q}${sig}`; +} + +// Load local blog images (as URLs) from the assets folder. +const _blogImageModules = import.meta.glob('../assets/blog-images/*', { eager: true, as: 'url' }) as Record; +const _blogImageUrls = Object.values(_blogImageModules); + +/** + * Pick a local blog image by numeric index. Images are assigned in order. + */ +export function getBlogImage(index?: number, fallback?: string) { + if (_blogImageUrls.length === 0) return fallback ?? ''; + if (typeof index !== 'number' || isNaN(index)) return _blogImageUrls[0]; + const idx = Math.abs(Math.floor(index)) % _blogImageUrls.length; + return _blogImageUrls[idx]; +} diff --git a/src/pages/BlogList.tsx b/src/pages/BlogList.tsx new file mode 100644 index 0000000..e3baae1 --- /dev/null +++ b/src/pages/BlogList.tsx @@ -0,0 +1,182 @@ +import { useEffect, useState } from 'react'; +import { Link, useSearchParams } from 'react-router-dom'; +import Header from '../components/landing/Header'; +import Footer from '../components/landing/Footer'; +import { Calendar, Clock, ArrowRight, Tag } from 'lucide-react'; +import { getAllPosts, type BlogPost } from '../lib/blogs'; +import { getBlogImage } from '../lib/utils'; +import './blog-list.css'; + +const categoryColors: Record = { + Strategy: 'bg-yellow-500/20 text-yellow-400', + Insights: 'bg-purple-500/20 text-purple-400', + Trends: 'bg-blue-500/20 text-blue-400', + Growth: 'bg-green-500/20 text-green-400', + Tutorial: 'bg-orange-500/20 text-orange-400', +}; + +const BlogList = () => { + const [searchParams] = useSearchParams(); + const [posts, setPosts] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const previewValue = (searchParams.get('preview') || '').toLowerCase(); + const isPreview = previewValue === '1' || previewValue === 'true' || previewValue === 'yes'; + + useEffect(() => { + let active = true; + setLoading(true); + setError(null); + + try { + const all = getAllPosts(); + if (!active) return; + // If frontmatter contains `live` flag, respect it; otherwise show all + const filtered = isPreview ? all : (all as any).filter ? (all as any).filter((p: any) => p.live !== false) : all; + setPosts(filtered as BlogPost[]); + } catch (err: any) { + setError(err?.message || 'Failed to load posts.'); + } finally { + if (active) setLoading(false); + } + + return () => { + active = false; + }; + }, [isPreview]); + + const [selectedCategory, setSelectedCategory] = useState('All'); + + const categories = ['All', ...Array.from(new Set(posts.map((p) => (p as any).category || 'General'))).filter(Boolean)]; + + const displayed = selectedCategory === 'All' ? posts : posts.filter((p) => ((p as any).category || 'General') === selectedCategory); + const featuredDisplayed = displayed.length ? displayed[0] : undefined; + const latest = displayed.length > 1 ? displayed.slice(1) : []; + + return ( +
+
+ +
+
+ {!loading && !featuredDisplayed && ( +
+ {error || 'No articles available yet.'} +
+ )} + + {featuredDisplayed && ( + +
+
+
+ {featuredDisplayed.title} +
+
+
+ {/** category may be undefined in current setup */} + + {(featuredDisplayed as any).category || 'General'} + + Featured +
+

+ {featuredDisplayed.title} +

+

{featuredDisplayed.description}

+
+ + + {featuredDisplayed.date} + + + + {/* readTime not available; omit if missing */} + {(featuredDisplayed as any).readTime || ''} + +
+ + Read Article + + +
+
+
+ + )} +
+ +
+
+

Latest Articles

+
+ + + +
+
+ +
+ {loading &&
Loading articles...
} + + {!loading && latest.length === 0 && !!featuredDisplayed && ( +
No additional articles yet.
+ )} + + {latest.map((p, i) => ( + +
+
+ {p.title} +
+
+
+ + {(p as any).category || 'General'} + +
+

{p.title}

+

{p.description}

+
+ + + {p.date} + + + + {(p as any).readTime || ''} + +
+
+
+ + ))} +
+
+
+ +
+
+ ); +}; + +export default BlogList; diff --git a/src/pages/BlogPost.tsx b/src/pages/BlogPost.tsx new file mode 100644 index 0000000..0d707f5 --- /dev/null +++ b/src/pages/BlogPost.tsx @@ -0,0 +1,83 @@ +import { useEffect, useState } from 'react'; +import { useParams, useSearchParams } from 'react-router-dom'; +import Header from '../components/landing/Header'; +import Footer from '../components/landing/Footer'; +import { getPostBySlug, getAllPosts, type BlogPost } from '../lib/blogs'; +import { marked } from 'marked'; +import { getBlogImage } from '../lib/utils'; +import { Calendar, Clock, Tag } from 'lucide-react'; +import './blog.css'; + +const BlogPost = () => { + const { slug } = useParams(); + const [searchParams] = useSearchParams(); + const [post, setPost] = useState(); + const [index, setIndex] = useState(undefined); + + useEffect(() => { + if (!slug) return; + const p = getPostBySlug(slug); + setPost(p); + if (p) { + const posts = getAllPosts(); + const idx = posts.findIndex((x) => x.slug === p.slug); + setIndex(idx >= 0 ? idx : undefined); + } + }, [slug]); + + if (!post) { + return ( +
+
+
+
+
Post not found.
+
+
+
+
+ ); + } + + const isPreview = ['1', 'true', 'yes'].includes((searchParams.get('preview') || '').toLowerCase()); + + return ( +
+
+ +
+
+
+
+ {index !== undefined && ( + {post.title} + )} +
+
+ {(post as any).category || 'General'} +
{post.date} {isPreview ? '(Preview)' : ''}
+
+ +

{post.title}

+
+ {post.date} + {(post as any).readTime || ''} +
+ +
+
+
+
+
+
+ +
+
+ ); +}; + +export default BlogPost; diff --git a/src/pages/blog-list.css b/src/pages/blog-list.css new file mode 100644 index 0000000..12cc6b6 --- /dev/null +++ b/src/pages/blog-list.css @@ -0,0 +1,35 @@ +.featured-card { + position: relative; + display: block; +} + +.featured-card .featured-meta { + position: absolute; + left: 0; + bottom: 0; + right: 0; + background: linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.45) 40%, rgba(0,0,0,0.7) 100%); + color: white; +} + +/* Slightly lift cards on hover */ +.bento-card:hover { + transform: translateY(-6px); + box-shadow: 0 12px 40px rgba(0,0,0,0.45); +} + +/* Make featured card text pop on dark backgrounds */ +.featured-card h2, +.featured-card p { + color: var(--color-text-primary); +} + +.featured-card img { + display: block; +} + +@media (min-width: 768px) { + .featured-card .featured-meta { + padding: 2rem 3rem; + } +} diff --git a/src/pages/blog.css b/src/pages/blog.css new file mode 100644 index 0000000..0bf021a --- /dev/null +++ b/src/pages/blog.css @@ -0,0 +1,109 @@ +/* Simple blog content styles to improve readability for markdown-rendered posts */ +.post-content { + color: var(--color-text-primary); + line-height: 1.8; + font-size: 1rem; + /* ensure nice spacing for paragraphs and headings */ + word-break: break-word; +} + +.post-content img { + display: block; + max-width: 100%; + border-radius: 0.5rem; + margin: 1rem 0; + box-shadow: 0 8px 24px rgba(0,0,0,0.45); +} + +.post-content h2 { + font-size: 1.6rem; + margin-top: 1.6rem; + margin-bottom: 0.5rem; + color: var(--color-accent-primary); + line-height: 1.15; +} + +.post-content h3 { + margin-top: 1.1rem; + margin-bottom: 0.45rem; +} + +.post-content h1 { + font-size: 2rem; + color: var(--color-accent-primary); + margin-bottom: 0.6rem; +} + +.post-content p { + margin: 0.6rem 0 0.9rem 0; + color: var(--color-text-muted); + font-size: 1.02rem; +} + +.post-content blockquote { + border-left: 4px solid rgba(255,255,255,0.12); + padding: 0.75rem 1rem; + margin: 1rem 0; + color: var(--color-text-secondary); + background: rgba(255,255,255,0.02); + border-radius: 6px; +} + +.post-content pre { + background: linear-gradient(180deg, rgba(0,0,0,0.55), rgba(0,0,0,0.45)); + color: #e6ffed; + padding: 1rem; + border-radius: 8px; + overflow: auto; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, 'Roboto Mono', 'Courier New', monospace; + font-size: 0.92rem; +} + +.post-content code { + background: rgba(0,0,0,0.28); + color: #e6ffed; + padding: 0.18rem 0.4rem; + border-radius: 6px; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, 'Roboto Mono', 'Courier New', monospace; +} + +.post-content ul, +.post-content ol { + margin-left: 1.25rem; + margin-bottom: 0.75rem; + padding-left: 1rem; + list-style-position: outside; +} + +.post-content ul { + list-style-type: disc; +} + +.post-content ol { + list-style-type: decimal; +} + +.post-content li { + margin-bottom: 0.45rem; +} + +.post-content li::marker { + color: var(--color-text-muted); +} + +.post-content a { + color: var(--color-accent-secondary); + text-decoration: none; + border-bottom: 1px dashed rgba(212,255,94,0.18); +} + +.post-content a:hover { + color: var(--color-accent-primary); + border-bottom-color: rgba(255,254,119,0.35); +} + +.post-meta { + color: var(--color-text-secondary); + font-size: 0.95rem; + margin-bottom: 0.75rem; +} diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 11f02fe..f756fa1 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1 +1,5 @@ /// +declare module '*.md' { + const content: string; + export default content; +}