diff --git a/Planteer/Planteer/__init__.py b/Planteer/Planteer/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Planteer/Planteer/asgi.py b/Planteer/Planteer/asgi.py
new file mode 100644
index 0000000..7693f2d
--- /dev/null
+++ b/Planteer/Planteer/asgi.py
@@ -0,0 +1,16 @@
+"""
+ASGI config for Planteer project.
+
+It exposes the ASGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/6.0/howto/deployment/asgi/
+"""
+
+import os
+
+from django.core.asgi import get_asgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Planteer.settings')
+
+application = get_asgi_application()
diff --git a/Planteer/Planteer/settings.py b/Planteer/Planteer/settings.py
new file mode 100644
index 0000000..2a9caf0
--- /dev/null
+++ b/Planteer/Planteer/settings.py
@@ -0,0 +1,126 @@
+"""
+Django settings for Planteer project.
+
+Generated by 'django-admin startproject' using Django 6.0.3.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/6.0/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/6.0/ref/settings/
+"""
+
+from pathlib import Path
+
+# Build paths inside the project like this: BASE_DIR / 'subdir'.
+BASE_DIR = Path(__file__).resolve().parent.parent
+
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = 'django-insecure-ys#o(!0#1*q4-6u6&_mj_%w2g(7x5e+j^d8ek196fn-_5nih#@'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+
+ALLOWED_HOSTS = []
+
+
+# Application definition
+
+INSTALLED_APPS = [
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ 'core',
+ 'plants',
+]
+
+MIDDLEWARE = [
+ 'django.middleware.security.SecurityMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'Planteer.urls'
+
+TEMPLATES = [
+ {
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': [],
+ 'DIRS': ['templates'],
+ 'APP_DIRS': True,
+ 'OPTIONS': {
+ 'context_processors': [
+ 'django.template.context_processors.request',
+ 'django.contrib.auth.context_processors.auth',
+ 'django.contrib.messages.context_processors.messages',
+ ],
+ },
+ },
+]
+
+WSGI_APPLICATION = 'Planteer.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/6.0/ref/settings/#databases
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': BASE_DIR / 'db.sqlite3',
+ }
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/6.0/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+ {
+ 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+ },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/6.0/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/6.0/howto/static-files/
+
+STATIC_URL = 'static/'
+
+STATICFILES_DIRS = [
+ BASE_DIR / "static"
+]
+MEDIA_URL = '/media/'
+MEDIA_ROOT = 'media'
\ No newline at end of file
diff --git a/Planteer/Planteer/urls.py b/Planteer/Planteer/urls.py
new file mode 100644
index 0000000..28dc336
--- /dev/null
+++ b/Planteer/Planteer/urls.py
@@ -0,0 +1,12 @@
+
+from django.contrib import admin
+from django.urls import path, include
+from django.conf import settings
+from django.conf.urls.static import static
+
+urlpatterns = [
+ path('', include('core.urls')),
+ path('plants/', include('plants.urls')),
+ path('admin/', admin.site.urls),
+]
+urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
diff --git a/Planteer/Planteer/wsgi.py b/Planteer/Planteer/wsgi.py
new file mode 100644
index 0000000..f14ac6f
--- /dev/null
+++ b/Planteer/Planteer/wsgi.py
@@ -0,0 +1,16 @@
+"""
+WSGI config for Planteer project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/6.0/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Planteer.settings')
+
+application = get_wsgi_application()
diff --git a/Planteer/core/__init__.py b/Planteer/core/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Planteer/core/admin.py b/Planteer/core/admin.py
new file mode 100644
index 0000000..b5d6b24
--- /dev/null
+++ b/Planteer/core/admin.py
@@ -0,0 +1,11 @@
+from django.contrib import admin
+from .models import Contact
+
+# Register your models here.
+
+
+class ContactAdmin(admin.ModelAdmin):
+ list_display = ('first_name', 'last_name', 'email', 'created_at')
+ list_filter = ('created_at',)
+
+admin.site.register(Contact, ContactAdmin)
\ No newline at end of file
diff --git a/Planteer/core/apps.py b/Planteer/core/apps.py
new file mode 100644
index 0000000..26f78a8
--- /dev/null
+++ b/Planteer/core/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class CoreConfig(AppConfig):
+ name = 'core'
diff --git a/Planteer/core/migrations/0001_initial.py b/Planteer/core/migrations/0001_initial.py
new file mode 100644
index 0000000..e5a2c74
--- /dev/null
+++ b/Planteer/core/migrations/0001_initial.py
@@ -0,0 +1,25 @@
+# Generated by Django 6.0.3 on 2026-04-20 17:42
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Contact',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('first_name', models.CharField(max_length=100)),
+ ('last_name', models.CharField(max_length=100)),
+ ('email', models.EmailField(max_length=254)),
+ ('message', models.TextField()),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ],
+ ),
+ ]
diff --git a/Planteer/core/migrations/__init__.py b/Planteer/core/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Planteer/core/models.py b/Planteer/core/models.py
new file mode 100644
index 0000000..cf8c5e8
--- /dev/null
+++ b/Planteer/core/models.py
@@ -0,0 +1,14 @@
+from django.db import models
+
+# Create your models here.
+
+class Contact(models.Model):
+ first_name = models.CharField(max_length=100)
+ last_name = models.CharField(max_length=100)
+ email = models.EmailField()
+ message = models.TextField()
+ created_at = models.DateTimeField(auto_now_add=True)
+
+ def __str__(self):
+ return f"{self.first_name} {self.last_name}"
+
\ No newline at end of file
diff --git a/Planteer/core/templates/core/article.html b/Planteer/core/templates/core/article.html
new file mode 100644
index 0000000..9212633
--- /dev/null
+++ b/Planteer/core/templates/core/article.html
@@ -0,0 +1,39 @@
+{% extends 'Planteer/base.html' %}
+{% block content %}
+
+
+
+
+
+
{{ article.tag }}
+
{{ article.title }}
+
+ {{ article.author }}
+ {{ article.read_time }}
+ {{ article.date }}
+
+
+
+
+
+
+
+
+
+
+
+ ← Back to Care Tips
+
+
+ {% for section in article.content %}
+
+
{{ section.heading }}
+
{{ section.text }}
+
+ {% endfor %}
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/Planteer/core/templates/core/care_tips.html b/Planteer/core/templates/core/care_tips.html
new file mode 100644
index 0000000..10b9e5b
--- /dev/null
+++ b/Planteer/core/templates/core/care_tips.html
@@ -0,0 +1,295 @@
+{% extends 'Planteer/base.html' %}
+{% block content %}
+
+
+
+
+
PLANT KNOWLEDGE HUB
+
Plant Care Tips
+
+ Expert guides on watering, repotting, pest control, and more —
+ everything you need to keep your plants thriving.
+
+
+ Watering Guide
+ Repotting
+ Pest Control
+ Lighting
+ Fertilizing
+
+
+
+
+
+
+
+
+
+
+
+
+
Check soil moisture before every watering — never water on a schedule blindly.
+
+
+
+
+
+
+
Rotate plants 90° every 2 weeks so all sides get equal light exposure.
+
+
+
+
+
+
+
Always use clean, sharp scissors for pruning — dull blades crush stems and invite disease.
+
+
+
+
+
+
+
Most houseplants prefer temperatures between 60–80°F (15–27°C). Avoid cold drafts.
+
+
+
+
+
+
+
+
+
+
+
+
Care Guides
+
+
+
+
+
+
+
+
+
Watering Guide
+
Learn how to water your plants correctly. Overwatering is the #1 killer of houseplants.
+
+ Check soil moisture with your finger
+ Water deeply but infrequently
+ Use room temperature water
+ Empty saucers after 30 minutes
+
+
+
+
+
+
+
+
+
+
Lighting Guide
+
Different plants need different light levels. Understanding light is key to plant success.
+
+ Bright direct: succulents & cacti
+ Bright indirect: most tropicals
+ Low light: ferns & snake plants
+ Rotate for even growth
+
+
+
+
+
+
+
+
+
+
Repotting Guide
+
Know when and how to repot your plants to keep them healthy and growing strong.
+
+ Repot every 1-2 years
+ Choose a pot 2" larger
+ Spring is the best time
+ Use fresh potting mix
+
+
+
+
+
+
+
+
+
+
Pest Control
+
Common pests can destroy your plants. Early detection and treatment is essential.
+
+ Inspect leaves regularly
+ Use neem oil for prevention
+ Isolate infected plants
+ Wipe leaves with damp cloth
+
+
+
+
+
+
+
+
+
+
Fertilizing
+
Feed your plants the right nutrients to promote healthy growth and vibrant foliage.
+
+ Fertilize in spring & summer
+ Use balanced NPK fertilizer
+ Never fertilize dry soil
+ Less is more — avoid overfeed
+
+
+
+
+
+
+
+
+
+
Humidity & Air
+
Many tropical plants need humidity. Learn how to create the perfect environment.
+
+ Mist leaves in dry weather
+ Use a pebble tray with water
+ Group plants together
+ Avoid air vents & heaters
+
+
+
+
+
+
+
+
+
+
+
+
+
Latest Articles
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/Planteer/core/templates/core/contact.html b/Planteer/core/templates/core/contact.html
new file mode 100644
index 0000000..a1bbd2f
--- /dev/null
+++ b/Planteer/core/templates/core/contact.html
@@ -0,0 +1,77 @@
+{% extends 'Planteer/base.html' %}
+{% block content %}
+
+
+
+
+
+
+
+
GET IN TOUCH
+
Contact Us
+
Have a question or suggestion? We'd love to hear from you.
+
+
+ {% if request.GET.sent %}
+
+ ✅ Message sent successfully!
+
+ {% endif %}
+
+ {% if errors %}
+
+ {% for error in errors %}
+
⚠️ {{ error }}
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/Planteer/core/templates/core/favourites.html b/Planteer/core/templates/core/favourites.html
new file mode 100644
index 0000000..56866a1
--- /dev/null
+++ b/Planteer/core/templates/core/favourites.html
@@ -0,0 +1,47 @@
+{% extends 'Planteer/base.html' %}
+{% block content %}
+
+
+
+
+
+
Home
+
›
+
Favourites
+
+
+
+
+
My Favourites
+
Plants you have saved
+
+
Browse All Plants →
+
+
+
+ {% if plants %}
+
+ {% for plant in plants %}
+
+ {% include "plants/partials/plant_card.html" with plant=plant %}
+
+ {% endfor %}
+
+
+ {% else %}
+
+
+
+
+
+
No favourites yet
+
Press the heart icon on any plant to save it here
+
+ Browse Plants
+
+
+ {% endif %}
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/Planteer/core/templates/core/find_my_plant.html b/Planteer/core/templates/core/find_my_plant.html
new file mode 100644
index 0000000..dfeed3d
--- /dev/null
+++ b/Planteer/core/templates/core/find_my_plant.html
@@ -0,0 +1,186 @@
+{% extends 'Planteer/base.html' %}
+{% block content %}
+
+
+
+
+
+
+
PERSONALIZED FOR YOU
+
Find Your Perfect Plant
+
+ Answer 3 quick questions about your space, lifestyle, and experience
+ — we'll match you with the plants that will actually thrive with you.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
3 Quick Questions
+
Light, watering habits, and experience level
+
+
+
+
+
+
+
+
+
Smart Matching
+
Our algorithm scores all 16 plants against your answers
+
+
+
+
+
+
+
+
+
4 Perfect Picks
+
Get your top matches with reasons why
+
+
+
+
+
+
+ Start Quiz Now
+
+
+
+
+
+
+
+
+
+
+
Find My Plant 🌿
+
+
+
+
+
+
1. How much natural light does your space get?
+
+
+
+
+
+
2. How often do you want to water your plant?
+
+
+
+
+
+
3. What is your experience with plants?
+
+
+
+
+
+ Find My Plant!
+
+
+
+
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/Planteer/core/templates/core/find_my_plant_results.html b/Planteer/core/templates/core/find_my_plant_results.html
new file mode 100644
index 0000000..a526581
--- /dev/null
+++ b/Planteer/core/templates/core/find_my_plant_results.html
@@ -0,0 +1,83 @@
+{% extends 'Planteer/base.html' %}
+{% block content %}
+
+
+
+
+
+
YOUR MATCHES
+
Your Perfect Plants
+
Based on your answers, here are the best plants for you
+
+
+
+ {% if plants %}
+
+ {% for plant in plants %}
+
+
+
+
+ {{ plant.get_category_display }}
+ {% if plant.is_edible %}
+ Edible
+ {% endif %}
+
+
+ {% if plant.image %}
+
+ {% endif %}
+
+
+
{{ plant.name }}
+
{{ plant.about|truncatechars:80 }}
+
+
+ {% if plant.water %}
+ {{ plant.get_water_display }}
+ {% endif %}
+ {% if plant.light %}
+ {{ plant.get_light_display }}
+ {% endif %}
+ {% if plant.care_level %}
+ {{ plant.get_care_level_display }}
+ {% endif %}
+
+
+
+ View Details →
+
+
+
+
+
+ {% endfor %}
+
+
+ {% else %}
+
+
+
+
+
+
No exact matches found
+
Try different answers or browse all our plants
+
+
+ {% endif %}
+
+
+ {% if plants %}
+
+ {% endif %}
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/Planteer/core/templates/core/home.html b/Planteer/core/templates/core/home.html
new file mode 100644
index 0000000..3d985ab
--- /dev/null
+++ b/Planteer/core/templates/core/home.html
@@ -0,0 +1,274 @@
+{% extends 'Planteer/base.html' %}
+
+{% block content %}
+
+
+
+
+
+
+
+
+
PLANT DATABASE
+
Planteer
+
Plant Database For Plants Lovers
+
+ Discover hundreds of beautiful plants, learn how to care for them,
+ and explore their uses. Your ultimate botanical reference.
+
+
+
+
+
+
+ Search
+
+
+
+
+ Tropical
+ Herbs
+ Succulents
+ Flowering
+
+
+
+
+
+
+
+
+
+
+
16+
+ Plant Species
+
+
+
+
+
+
+
+
+
6
+ Categories
+
+
+
+
+
+
+
+
+
100%
+ Expert Curated
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Plant Categories
+
+
+
+
+
+
+
+
+
+
ALL Plants
+
Learn more about plants
+
+
More →
+
+
+
+ {% for plant in plants %}
+
+
+ {% if plant.image %}
+
+ {% endif %}
+
+
{{ plant.name }}
+
{{ plant.about|truncatechars:50 }}
+
{{ plant.get_category_display }}
+
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LATEST ADDITIONS
+
Recently Added Plants
+
+
+ View All Plants →
+
+
+
+
+
+ {% for plant in recent_plants %}
+
+ {% include "plants/partials/plant_card_simple.html" with plant=plant %}
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
WHY PLANTEER
+
More Than Just Plants
+
+
+
+
+
+
+
Curated Collection
+
Every plant in our catalog is carefully selected for quality, beauty, ease of care.
+
+
+
+
+
+
+
Expert Guidance
+
Detailed care instructions and tips from experienced botanists for every plant.
+
+
+
+
+
+
+
Easy Discovery
+
Filter by category, edibility, and more to find the perfect plant for your space.
+
+
+
+
+
+
+
Plant Community
+
Join thousands of plant lovers sharing their growing journeys and experiences.
+
+
+
+
+
+
+
+
+
+
+
+
+
WHAT PEOPLE SAY
+
Loved by Plant Lovers
+
+
+
+
+
+
+
+
+
It's transformed my apartment. I went from zero plants to a full indoor jungle.
+
+
+
+
Abdulmalik Aqeel
+ Interior Designer
+
+
+
+
+
+
+
The plant detail pages are incredibly informative.
+
+
+
+
Arwa Mohammed
+ Plant Enthusiast
+
+
+
+
+
+
+
+
+
+
+
+
Amazing experience! The app is clean and simple.
+
+
+
+
Sarah Ali
+ Designer
+
+
+
+
+
+
+
I discovered many new plants thanks to this platform!
+
+
+
+
Khalid Ahmed
+ Gardener
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/Planteer/core/templates/core/login.html b/Planteer/core/templates/core/login.html
new file mode 100644
index 0000000..29cd6da
--- /dev/null
+++ b/Planteer/core/templates/core/login.html
@@ -0,0 +1,59 @@
+{% extends 'Planteer/base.html' %}
+{% block content %}
+
+
+
+
+
+
+
+
+
+
+
+
+
Welcome Back
+
Sign in to your Planteer account
+
+
+
+ {% if error %}
+
+ ⚠️ {{ error }}
+
+ {% endif %}
+
+
+
+ {% csrf_token %}
+
+
+ Username
+
+
+
+
+ Password
+
+
+
+
+ Sign In 🌿
+
+
+
+
+
+ Don't have an account?
+ Create one
+
+
+
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/Planteer/core/templates/core/messages.html b/Planteer/core/templates/core/messages.html
new file mode 100644
index 0000000..445dbf3
--- /dev/null
+++ b/Planteer/core/templates/core/messages.html
@@ -0,0 +1,46 @@
+{% extends 'Planteer/base.html' %}
+{% block content %}
+
+
+
+
Messages
+
+
+
+ {% for msg in contacts %}
+
+
+
+
+
{{ msg.message }}
+
{{ msg.created_at|date:"d M Y" }}
+
+
+
+ {% empty %}
+
No messages yet
+ {% endfor %}
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/Planteer/core/templates/core/register.html b/Planteer/core/templates/core/register.html
new file mode 100644
index 0000000..74d6004
--- /dev/null
+++ b/Planteer/core/templates/core/register.html
@@ -0,0 +1,65 @@
+{% extends 'Planteer/base.html' %}
+{% block content %}
+
+
+
+
+
+
+
+
+
+
+
+
+
Create Account
+
Join Planteer and start exploring plants
+
+
+
+ {% if error %}
+
+ ⚠️ {{ error }}
+
+ {% endif %}
+
+
+
+ {% csrf_token %}
+
+
+ Username
+
+
+
+
+ Email
+
+
+
+
+ Password
+
+
+
+
+ Create Account 🌿
+
+
+
+
+
+ Already have an account?
+ Sign in
+
+
+
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/Planteer/core/tests.py b/Planteer/core/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/Planteer/core/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/Planteer/core/urls.py b/Planteer/core/urls.py
new file mode 100644
index 0000000..3ee911d
--- /dev/null
+++ b/Planteer/core/urls.py
@@ -0,0 +1,31 @@
+from django.urls import path
+from . import views
+
+app_name = "core"
+
+urlpatterns = [
+ path('', views.home, name='home'),
+
+ path('contact/', views.contact, name='contact'),
+
+ path('messages/', views.messages_view, name='messages'),
+
+ path('toggle-dark-mode/', views.toggle_dark_mode, name='toggle_dark_mode'),
+
+ path('care-tips/', views.care_tips_view, name='care_tips'),
+
+ path('care-tips/
/', views.article_view, name='article'),
+
+ path('find-my-plant/', views.find_my_plant_view, name='find_my_plant'),
+
+ path('find-my-plant/results/', views.find_my_plant_results, name='find_my_plant_results'),
+
+ path('favourites/', views.favourites_view, name='favourites'),
+
+ path('register/', views.register_view, name='register'),
+
+ path('login/', views.login_view, name='login'),
+
+ path('logout/', views.logout_view, name='logout'),
+
+]
\ No newline at end of file
diff --git a/Planteer/core/views.py b/Planteer/core/views.py
new file mode 100644
index 0000000..c4d635b
--- /dev/null
+++ b/Planteer/core/views.py
@@ -0,0 +1,241 @@
+from django.shortcuts import render
+from plants.models import Plant
+from django.shortcuts import render, redirect
+from .models import Contact
+
+
+
+# Create your views here.
+
+def home_view(request):
+ return render(request, 'core/home.html')
+
+def contact_view(request):
+ return render(request, 'core/contact.html')
+
+
+def home(request):
+ plants = Plant.objects.all()[:3]
+ recent_plants = Plant.objects.all().order_by('-created_at')[:3]
+
+ return render(request, 'core/home.html', {
+ 'plants': plants,
+ 'recent_plants': recent_plants,
+ })
+
+
+
+def contact(request):
+ errors = []
+ form_data = {}
+
+ if request.method == 'POST':
+ first_name = request.POST.get('first_name', '').strip()
+ last_name = request.POST.get('last_name', '').strip()
+ email = request.POST.get('email', '').strip()
+ message = request.POST.get('message', '').strip()
+
+ form_data = {
+ 'first_name': first_name,
+ 'last_name': last_name,
+ 'email': email,
+ 'message': message,
+ }
+
+ if not first_name:
+ errors.append('First name is required')
+ if not last_name:
+ errors.append('Last name is required')
+ if not email or '@' not in email:
+ errors.append('Valid email is required')
+ if not message:
+ errors.append('Message is required')
+
+ if not errors:
+ Contact.objects.create(**form_data)
+ return redirect('/contact/?sent=1')
+
+ return render(request, 'core/contact.html', {
+ 'errors': errors,
+ 'form_data': form_data,
+ })
+
+def messages_view(request):
+ contacts = Contact.objects.all().order_by('-created_at')
+
+ return render(request, 'core/messages.html', {
+ 'contacts': contacts
+ })
+
+
+
+
+
+def toggle_dark_mode(request):
+ redirect_url = request.GET.get('next', '/')
+ response = redirect(redirect_url)
+
+ current = request.COOKIES.get('dark_mode', 'off')
+ if current == 'on':
+ response.set_cookie('dark_mode', 'off', max_age=60*60*24*30)
+ else:
+ response.set_cookie('dark_mode', 'on', max_age=60*60*24*30)
+
+ return response
+
+
+def care_tips_view(request):
+ return render(request, 'core/care_tips.html')
+
+
+ARTICLES = {
+ 'watering-guide': {
+ 'title': 'The Complete Watering Guide for Houseplants',
+ 'tag': 'Watering',
+ 'author': 'Lily',
+ 'read_time': '7 min read',
+ 'date': 'Mar 12',
+ 'image': 'https://images.unsplash.com/photo-1416879595882-3373a0480b5b?w=1200',
+ 'content': [
+ {'heading': 'The Golden Rule', 'text': 'Never water on a schedule. Always check the soil moisture first by sticking your finger 2 inches into the soil. If it feels dry, water deeply. If still moist, wait another day or two.'},
+ {'heading': 'How to Water Correctly', 'text': 'Water slowly and deeply until it drains from the bottom. This encourages roots to grow downward. Avoid shallow watering which leads to weak root systems.'},
+ {'heading': 'Common Mistakes', 'text': 'Overwatering is the number one killer of houseplants. Signs include yellowing leaves, mushy stems, and soggy soil. Underwatering shows as dry, crispy leaf edges and wilting.'},
+ {'heading': 'Water Quality', 'text': 'Most plants prefer room temperature water. Let tap water sit overnight to allow chlorine to evaporate. Rainwater or filtered water is ideal for sensitive plants like orchids.'},
+ ]
+ },
+ 'repotting-guide': {
+ 'title': 'When & How to Repot Your Plants the Right Way',
+ 'tag': 'Repotting',
+ 'author': 'Marcus',
+ 'read_time': '8 min read',
+ 'date': 'Mar 28',
+ 'image': 'https://images.unsplash.com/photo-1466692476868-aef1dfb1e735?w=1200',
+ 'content': [
+ {'heading': 'When to Repot', 'text': 'Signs your plant needs repotting: roots growing out of drainage holes, plant drying out too quickly, or roots circling the bottom of the pot. Spring is the best time to repot.'},
+ {'heading': 'Choosing the Right Pot', 'text': 'Always go up just one size — about 2 inches larger in diameter. Too large a pot holds excess moisture and can lead to root rot.'},
+ {'heading': 'The Repotting Process', 'text': 'Water your plant the day before. Gently remove it, shake off old soil, trim any dead roots, and place in fresh potting mix. Water lightly after repotting.'},
+ {'heading': 'After Care', 'text': 'Keep the plant in indirect light for 1-2 weeks after repotting. Avoid fertilizing for at least a month to let roots settle in the new soil.'},
+ ]
+ },
+ 'soil-guide': {
+ 'title': 'The Best Soil Mixes for Every Plant Type',
+ 'tag': 'Soil & Nutrients',
+ 'author': 'Lily',
+ 'read_time': '6 min read',
+ 'date': 'Apr 2',
+ 'image': 'https://images.unsplash.com/photo-1585320806297-9794b3e4eeae?w=1200',
+ 'content': [
+ {'heading': 'Why Soil Matters', 'text': 'Soil is the foundation of plant health. It provides nutrients, support, and regulates moisture. Using the wrong soil type is one of the most common mistakes.'},
+ {'heading': 'For Succulents & Cacti', 'text': 'Use a fast-draining mix with extra perlite or coarse sand. Standard potting soil retains too much moisture and will cause root rot in succulents.'},
+ {'heading': 'For Tropical Plants', 'text': 'A rich, well-draining mix works best. Combine potting soil with perlite and orchid bark for good aeration and moisture retention.'},
+ {'heading': 'For Herbs', 'text': 'Herbs prefer a light, fertile mix with good drainage. Add compost for extra nutrients. Avoid heavy clay soils that compact easily.'},
+ ]
+ },
+ 'propagation-guide': {
+ 'title': 'How to Propagate Any Plant at Home',
+ 'tag': 'Propagation',
+ 'author': 'Sara',
+ 'read_time': '5 min read',
+ 'date': 'Apr 8',
+ 'image': 'https://images.unsplash.com/photo-1518531933037-91b2f5f229cc?w=1200',
+ 'content': [
+ {'heading': 'What is Propagation', 'text': 'Propagation is growing new plants from an existing one. It is free, fun, and a great way to expand your collection or share plants with friends.'},
+ {'heading': 'Stem Cuttings', 'text': 'Cut a healthy stem just below a node. Remove lower leaves and place in water or moist soil. Keep in bright indirect light and change water every few days.'},
+ {'heading': 'Leaf Cuttings', 'text': 'Perfect for succulents. Gently twist a healthy leaf off and lay it on top of dry soil. Mist lightly every few days until roots and a new plant emerge.'},
+ {'heading': 'Division', 'text': 'For clumping plants, remove from pot and gently separate the root ball into sections. Each section should have roots and leaves. Pot up separately.'},
+ ]
+ },
+ 'seasonal-guide': {
+ 'title': 'Seasonal Plant Care: What to Do Each Month',
+ 'tag': 'Seasonal',
+ 'author': 'Marcus',
+ 'read_time': '9 min read',
+ 'date': 'Apr 15',
+ 'image': 'https://images.unsplash.com/photo-1534710961216-75c88202f43e?w=1200',
+ 'content': [
+ {'heading': 'Spring (Mar-May)', 'text': 'This is the growing season. Start fertilizing, repot if needed, and increase watering as days get longer. Great time to take cuttings and propagate.'},
+ {'heading': 'Summer (Jun-Aug)', 'text': 'Watch for heat stress and increase humidity. Water more frequently but check soil first. Move plants away from hot windows to avoid leaf scorch.'},
+ {'heading': 'Autumn (Sep-Nov)', 'text': 'Gradually reduce watering and stop fertilizing. Bring outdoor plants inside before temperatures drop. Check for pests before bringing them indoors.'},
+ {'heading': 'Winter (Dec-Feb)', 'text': 'Most plants go dormant. Water sparingly and avoid fertilizing. Keep plants away from cold drafts and heating vents. Dust leaves to maximize light absorption.'},
+ ]
+ },
+}
+
+def article_view(request, slug):
+ article = ARTICLES.get(slug)
+ if not article:
+ return redirect('/care-tips/')
+ return render(request, 'core/article.html', {'article': article})
+
+def find_my_plant_view(request):
+ return render(request, 'core/find_my_plant.html')
+
+def find_my_plant_results(request):
+ light = request.GET.get('light')
+ water = request.GET.get('water')
+ care = request.GET.get('care')
+
+ from plants.models import Plant
+ plants = Plant.objects.all()
+
+ if light:
+ plants = plants.filter(light=light)
+ if water:
+ plants = plants.filter(water=water)
+ if care:
+ plants = plants.filter(care_level=care)
+
+ return render(request, 'core/find_my_plant_results.html', {
+ 'plants': plants,
+ 'light': light,
+ 'water': water,
+ 'care': care,
+ })
+
+
+def favourites_view(request):
+ from plants.models import Plant
+ favs = request.COOKIES.get('favs', '')
+ fav_ids = [f for f in favs.split(',') if f]
+ plants = Plant.objects.filter(id__in=fav_ids)
+ return render(request, 'core/favourites.html', {'plants': plants})
+
+
+from django.contrib.auth import authenticate, login, logout
+from django.contrib.auth.models import User
+
+def register_view(request):
+ if request.method == 'POST':
+ username = request.POST.get('username')
+ email = request.POST.get('email')
+ password = request.POST.get('password')
+
+ if User.objects.filter(username=username).exists():
+ return render(request, 'core/register.html', {'error': 'Username already exists'})
+
+ user = User.objects.create_user(username=username, email=email, password=password)
+ user.save()
+ login(request, user)
+ return redirect('/')
+
+ return render(request, 'core/register.html')
+
+
+def login_view(request):
+ if request.method == 'POST':
+ username = request.POST.get('username')
+ password = request.POST.get('password')
+
+ user = authenticate(request, username=username, password=password)
+ if user is not None:
+ login(request, user)
+ return redirect('/')
+ else:
+ return render(request, 'core/login.html', {'error': 'Invalid username or password'})
+
+ return render(request, 'core/login.html')
+
+
+def logout_view(request):
+ logout(request)
+ return redirect('/')
diff --git a/Planteer/db.sqlite3 b/Planteer/db.sqlite3
new file mode 100644
index 0000000..6f42b14
Binary files /dev/null and b/Planteer/db.sqlite3 differ
diff --git a/Planteer/manage.py b/Planteer/manage.py
new file mode 100644
index 0000000..bd0a64a
--- /dev/null
+++ b/Planteer/manage.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+"""Django's command-line utility for administrative tasks."""
+import os
+import sys
+
+
+def main():
+ """Run administrative tasks."""
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Planteer.settings')
+ try:
+ from django.core.management import execute_from_command_line
+ except ImportError as exc:
+ raise ImportError(
+ "Couldn't import Django. Are you sure it's installed and "
+ "available on your PYTHONPATH environment variable? Did you "
+ "forget to activate a virtual environment?"
+ ) from exc
+ execute_from_command_line(sys.argv)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/Planteer/media/flags/Flag_of_Brazil.svg.png b/Planteer/media/flags/Flag_of_Brazil.svg.png
new file mode 100644
index 0000000..69801c3
Binary files /dev/null and b/Planteer/media/flags/Flag_of_Brazil.svg.png differ
diff --git a/Planteer/media/flags/ae.webp b/Planteer/media/flags/ae.webp
new file mode 100644
index 0000000..785ab09
Binary files /dev/null and b/Planteer/media/flags/ae.webp differ
diff --git a/Planteer/media/flags/eg.webp b/Planteer/media/flags/eg.webp
new file mode 100644
index 0000000..c4bf2a2
Binary files /dev/null and b/Planteer/media/flags/eg.webp differ
diff --git a/Planteer/media/flags/india.jpg b/Planteer/media/flags/india.jpg
new file mode 100644
index 0000000..a9d9af9
Binary files /dev/null and b/Planteer/media/flags/india.jpg differ
diff --git a/Planteer/media/flags/iq.webp b/Planteer/media/flags/iq.webp
new file mode 100644
index 0000000..69ca23e
Binary files /dev/null and b/Planteer/media/flags/iq.webp differ
diff --git a/Planteer/media/flags/kw.webp b/Planteer/media/flags/kw.webp
new file mode 100644
index 0000000..1e64bef
Binary files /dev/null and b/Planteer/media/flags/kw.webp differ
diff --git a/Planteer/media/flags/mx.webp b/Planteer/media/flags/mx.webp
new file mode 100644
index 0000000..aa40287
Binary files /dev/null and b/Planteer/media/flags/mx.webp differ
diff --git a/Planteer/media/flags/saudi.jpg b/Planteer/media/flags/saudi.jpg
new file mode 100644
index 0000000..6e5b49d
Binary files /dev/null and b/Planteer/media/flags/saudi.jpg differ
diff --git a/Planteer/media/flags/south_africa.png b/Planteer/media/flags/south_africa.png
new file mode 100644
index 0000000..147ae6d
Binary files /dev/null and b/Planteer/media/flags/south_africa.png differ
diff --git a/Planteer/media/flags/usa.png b/Planteer/media/flags/usa.png
new file mode 100644
index 0000000..9d7da1c
Binary files /dev/null and b/Planteer/media/flags/usa.png differ
diff --git a/Planteer/media/plants/Echeveria.jpg b/Planteer/media/plants/Echeveria.jpg
new file mode 100644
index 0000000..01282ed
Binary files /dev/null and b/Planteer/media/plants/Echeveria.jpg differ
diff --git a/Planteer/media/plants/Fiddle_Leaf_Fig.webp b/Planteer/media/plants/Fiddle_Leaf_Fig.webp
new file mode 100644
index 0000000..07cdafa
Binary files /dev/null and b/Planteer/media/plants/Fiddle_Leaf_Fig.webp differ
diff --git a/Planteer/media/plants/Jasmine.jpg b/Planteer/media/plants/Jasmine.jpg
new file mode 100644
index 0000000..132c070
Binary files /dev/null and b/Planteer/media/plants/Jasmine.jpg differ
diff --git a/Planteer/media/plants/aloe_vera.jpg b/Planteer/media/plants/aloe_vera.jpg
new file mode 100644
index 0000000..1c9a0ba
Binary files /dev/null and b/Planteer/media/plants/aloe_vera.jpg differ
diff --git a/Planteer/media/plants/aloe_vera_1JvUJDd.jpg b/Planteer/media/plants/aloe_vera_1JvUJDd.jpg
new file mode 100644
index 0000000..1c9a0ba
Binary files /dev/null and b/Planteer/media/plants/aloe_vera_1JvUJDd.jpg differ
diff --git a/Planteer/media/plants/basil.jpg b/Planteer/media/plants/basil.jpg
new file mode 100644
index 0000000..549ff54
Binary files /dev/null and b/Planteer/media/plants/basil.jpg differ
diff --git a/Planteer/media/plants/bird_of_paradias.jpg b/Planteer/media/plants/bird_of_paradias.jpg
new file mode 100644
index 0000000..16c24d9
Binary files /dev/null and b/Planteer/media/plants/bird_of_paradias.jpg differ
diff --git a/Planteer/media/plants/bird_of_paradias_G3MJGan.jpg b/Planteer/media/plants/bird_of_paradias_G3MJGan.jpg
new file mode 100644
index 0000000..16c24d9
Binary files /dev/null and b/Planteer/media/plants/bird_of_paradias_G3MJGan.jpg differ
diff --git a/Planteer/media/plants/boston_fern.webp b/Planteer/media/plants/boston_fern.webp
new file mode 100644
index 0000000..2d59b03
Binary files /dev/null and b/Planteer/media/plants/boston_fern.webp differ
diff --git a/Planteer/media/plants/jade-plant.jpg b/Planteer/media/plants/jade-plant.jpg
new file mode 100644
index 0000000..e752446
Binary files /dev/null and b/Planteer/media/plants/jade-plant.jpg differ
diff --git a/Planteer/media/plants/maidenhair-fern.webp b/Planteer/media/plants/maidenhair-fern.webp
new file mode 100644
index 0000000..494e1fd
Binary files /dev/null and b/Planteer/media/plants/maidenhair-fern.webp differ
diff --git a/Planteer/media/plants/mint.jpg b/Planteer/media/plants/mint.jpg
new file mode 100644
index 0000000..30a4909
Binary files /dev/null and b/Planteer/media/plants/mint.jpg differ
diff --git a/Planteer/media/plants/monstera.jpg b/Planteer/media/plants/monstera.jpg
new file mode 100644
index 0000000..b2a792b
Binary files /dev/null and b/Planteer/media/plants/monstera.jpg differ
diff --git a/Planteer/media/plants/monstera_Ie0wWYH.jpg b/Planteer/media/plants/monstera_Ie0wWYH.jpg
new file mode 100644
index 0000000..b2a792b
Binary files /dev/null and b/Planteer/media/plants/monstera_Ie0wWYH.jpg differ
diff --git a/Planteer/media/plants/olive_tree.png b/Planteer/media/plants/olive_tree.png
new file mode 100644
index 0000000..2b88b6f
Binary files /dev/null and b/Planteer/media/plants/olive_tree.png differ
diff --git a/Planteer/media/plants/palm_tree.webp b/Planteer/media/plants/palm_tree.webp
new file mode 100644
index 0000000..3bab474
Binary files /dev/null and b/Planteer/media/plants/palm_tree.webp differ
diff --git a/Planteer/media/plants/rose.avif b/Planteer/media/plants/rose.avif
new file mode 100644
index 0000000..af9d054
Binary files /dev/null and b/Planteer/media/plants/rose.avif differ
diff --git a/Planteer/media/plants/rosemary.webp b/Planteer/media/plants/rosemary.webp
new file mode 100644
index 0000000..218e3e8
Binary files /dev/null and b/Planteer/media/plants/rosemary.webp differ
diff --git a/Planteer/media/plants/rosemary_UOglqzN.webp b/Planteer/media/plants/rosemary_UOglqzN.webp
new file mode 100644
index 0000000..02f2c0e
Binary files /dev/null and b/Planteer/media/plants/rosemary_UOglqzN.webp differ
diff --git a/Planteer/media/plants/rosemary_f4MYaBW.webp b/Planteer/media/plants/rosemary_f4MYaBW.webp
new file mode 100644
index 0000000..fa3bf09
Binary files /dev/null and b/Planteer/media/plants/rosemary_f4MYaBW.webp differ
diff --git a/Planteer/media/plants/sunflower.avif b/Planteer/media/plants/sunflower.avif
new file mode 100644
index 0000000..68c25f6
Binary files /dev/null and b/Planteer/media/plants/sunflower.avif differ
diff --git a/Planteer/media/plants/sunflower1.webp b/Planteer/media/plants/sunflower1.webp
new file mode 100644
index 0000000..632b86b
Binary files /dev/null and b/Planteer/media/plants/sunflower1.webp differ
diff --git a/Planteer/media/plants/wjn.logo.png b/Planteer/media/plants/wjn.logo.png
new file mode 100644
index 0000000..4339d25
Binary files /dev/null and b/Planteer/media/plants/wjn.logo.png differ
diff --git a/Planteer/media/plants/wjn_d28Ytpf.logo.png b/Planteer/media/plants/wjn_d28Ytpf.logo.png
new file mode 100644
index 0000000..4339d25
Binary files /dev/null and b/Planteer/media/plants/wjn_d28Ytpf.logo.png differ
diff --git a/Planteer/plants/__init__.py b/Planteer/plants/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Planteer/plants/admin.py b/Planteer/plants/admin.py
new file mode 100644
index 0000000..cca2c50
--- /dev/null
+++ b/Planteer/plants/admin.py
@@ -0,0 +1,24 @@
+from django.contrib import admin
+from .models import Plant, Comment, Country
+# Register your models here.
+
+
+
+
+class PlantAdmin(admin.ModelAdmin):
+ list_display = ('name', 'category', 'is_edible', 'created_at')
+ list_filter = ('category', 'is_edible')
+
+class CommentAdmin(admin.ModelAdmin):
+ list_display = ('name', 'plant', 'created_at')
+ list_filter = ('plant',)
+
+admin.site.register(Plant, PlantAdmin)
+admin.site.register(Comment, CommentAdmin)
+
+
+
+class CountryAdmin(admin.ModelAdmin):
+ list_display = ('name',)
+
+admin.site.register(Country, CountryAdmin)
\ No newline at end of file
diff --git a/Planteer/plants/apps.py b/Planteer/plants/apps.py
new file mode 100644
index 0000000..478c3ce
--- /dev/null
+++ b/Planteer/plants/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class PlantsConfig(AppConfig):
+ name = 'plants'
diff --git a/Planteer/plants/forms.py b/Planteer/plants/forms.py
new file mode 100644
index 0000000..22162c9
--- /dev/null
+++ b/Planteer/plants/forms.py
@@ -0,0 +1,7 @@
+from django import forms
+from .models import Plant
+
+class PlantForm(forms.ModelForm):
+ class Meta:
+ model = Plant
+ fields = '__all__'
\ No newline at end of file
diff --git a/Planteer/plants/migrations/0001_initial.py b/Planteer/plants/migrations/0001_initial.py
new file mode 100644
index 0000000..2e3f9d9
--- /dev/null
+++ b/Planteer/plants/migrations/0001_initial.py
@@ -0,0 +1,27 @@
+# Generated by Django 6.0.3 on 2026-04-19 05:55
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Plant',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=200)),
+ ('about', models.TextField()),
+ ('used_for', models.TextField()),
+ ('image', models.ImageField(upload_to='plants/')),
+ ('category', models.CharField(choices=[('tree', 'Tree'), ('fruit', 'Fruit'), ('vegetable', 'Vegetable'), ('herbs', 'Herbs'), ('flowering', 'Flowering')], max_length=20)),
+ ('is_edible', models.BooleanField(default=False)),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ],
+ ),
+ ]
diff --git a/Planteer/plants/migrations/0002_alter_plant_image.py b/Planteer/plants/migrations/0002_alter_plant_image.py
new file mode 100644
index 0000000..f25f6e4
--- /dev/null
+++ b/Planteer/plants/migrations/0002_alter_plant_image.py
@@ -0,0 +1,18 @@
+# Generated by Django 6.0.3 on 2026-04-19 17:21
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('plants', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='plant',
+ name='image',
+ field=models.ImageField(blank=True, null=True, upload_to='plants/'),
+ ),
+ ]
diff --git a/Planteer/plants/migrations/0003_contact_alter_plant_category.py b/Planteer/plants/migrations/0003_contact_alter_plant_category.py
new file mode 100644
index 0000000..a3bdc37
--- /dev/null
+++ b/Planteer/plants/migrations/0003_contact_alter_plant_category.py
@@ -0,0 +1,29 @@
+# Generated by Django 6.0.3 on 2026-04-19 19:21
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('plants', '0002_alter_plant_image'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Contact',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('first_name', models.CharField(max_length=100)),
+ ('last_name', models.CharField(max_length=100)),
+ ('email', models.EmailField(max_length=254)),
+ ('message', models.TextField()),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ],
+ ),
+ migrations.AlterField(
+ model_name='plant',
+ name='category',
+ field=models.CharField(choices=[('TR', 'Tropical'), ('SU', 'Succulents'), ('HE', 'Herbs'), ('FE', 'Ferns'), ('FL', 'Flowering'), ('TRS', 'Trees')], max_length=20),
+ ),
+ ]
diff --git a/Planteer/plants/migrations/0004_comment_delete_contact.py b/Planteer/plants/migrations/0004_comment_delete_contact.py
new file mode 100644
index 0000000..86af82f
--- /dev/null
+++ b/Planteer/plants/migrations/0004_comment_delete_contact.py
@@ -0,0 +1,27 @@
+# Generated by Django 6.0.3 on 2026-04-20 17:42
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('plants', '0003_contact_alter_plant_category'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Comment',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=100)),
+ ('content', models.TextField()),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ('plant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='plants.plant')),
+ ],
+ ),
+ migrations.DeleteModel(
+ name='Contact',
+ ),
+ ]
diff --git a/Planteer/plants/migrations/0005_country_plant_countries.py b/Planteer/plants/migrations/0005_country_plant_countries.py
new file mode 100644
index 0000000..1b96e00
--- /dev/null
+++ b/Planteer/plants/migrations/0005_country_plant_countries.py
@@ -0,0 +1,26 @@
+# Generated by Django 6.0.3 on 2026-04-21 09:20
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('plants', '0004_comment_delete_contact'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Country',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=100)),
+ ('flag', models.ImageField(blank=True, null=True, upload_to='flags/')),
+ ],
+ ),
+ migrations.AddField(
+ model_name='plant',
+ name='countries',
+ field=models.ManyToManyField(blank=True, related_name='plants', to='plants.country'),
+ ),
+ ]
diff --git a/Planteer/plants/migrations/0006_plant_care_level_plant_light_plant_water.py b/Planteer/plants/migrations/0006_plant_care_level_plant_light_plant_water.py
new file mode 100644
index 0000000..6f5d262
--- /dev/null
+++ b/Planteer/plants/migrations/0006_plant_care_level_plant_light_plant_water.py
@@ -0,0 +1,28 @@
+# Generated by Django 6.0.3 on 2026-04-22 21:39
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('plants', '0005_country_plant_countries'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='plant',
+ name='care_level',
+ field=models.CharField(blank=True, choices=[('EASY', 'Easy'), ('MED', 'Medium'), ('HARD', 'Hard')], max_length=10, null=True),
+ ),
+ migrations.AddField(
+ model_name='plant',
+ name='light',
+ field=models.CharField(blank=True, choices=[('DIR', 'Direct'), ('IND', 'Indirect'), ('LOW', 'Low Light')], max_length=10, null=True),
+ ),
+ migrations.AddField(
+ model_name='plant',
+ name='water',
+ field=models.CharField(blank=True, choices=[('LOW', 'Low'), ('MO', 'Moderate'), ('HIGH', 'High')], max_length=10, null=True),
+ ),
+ ]
diff --git a/Planteer/plants/migrations/0007_comment_user.py b/Planteer/plants/migrations/0007_comment_user.py
new file mode 100644
index 0000000..f94b76d
--- /dev/null
+++ b/Planteer/plants/migrations/0007_comment_user.py
@@ -0,0 +1,21 @@
+# Generated by Django 6.0.3 on 2026-04-26 18:07
+
+import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('plants', '0006_plant_care_level_plant_light_plant_water'),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='comment',
+ name='user',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+ ),
+ ]
diff --git a/Planteer/plants/migrations/__init__.py b/Planteer/plants/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Planteer/plants/models.py b/Planteer/plants/models.py
new file mode 100644
index 0000000..780ad38
--- /dev/null
+++ b/Planteer/plants/models.py
@@ -0,0 +1,63 @@
+from django.db import models
+from django.contrib.auth.models import User
+
+class Country(models.Model):
+ name = models.CharField(max_length=100)
+ flag = models.ImageField(upload_to='flags/', null=True, blank=True)
+
+ def __str__(self):
+ return self.name
+
+
+class Plant(models.Model):
+
+ class Category(models.TextChoices):
+ TR = 'TR', 'Tropical'
+ SU = 'SU', 'Succulents'
+ HE = 'HE', 'Herbs'
+ FE = 'FE', 'Ferns'
+ FL = 'FL', 'Flowering'
+ TRS = 'TRS', 'Trees'
+
+ class WaterLevel(models.TextChoices):
+ LOW = 'LOW', 'Low'
+ MODERATE = 'MO', 'Moderate'
+ HIGH = 'HIGH', 'High'
+
+ class LightLevel(models.TextChoices):
+ DIRECT = 'DIR', 'Direct'
+ INDIRECT = 'IND', 'Indirect'
+ LOW_LIGHT = 'LOW', 'Low Light'
+
+ class CareLevel(models.TextChoices):
+ EASY = 'EASY', 'Easy'
+ MEDIUM = 'MED', 'Medium'
+ HARD = 'HARD', 'Hard'
+
+ name = models.CharField(max_length=200)
+ about = models.TextField()
+ used_for = models.TextField()
+ image = models.ImageField(upload_to='plants/', null=True, blank=True)
+ category = models.CharField(max_length=20, choices=Category.choices)
+ is_edible = models.BooleanField(default=False)
+ created_at = models.DateTimeField(auto_now_add=True)
+ countries = models.ManyToManyField(Country, blank=True, related_name='plants')
+ water = models.CharField(max_length=10, choices=WaterLevel.choices, null=True, blank=True)
+ light = models.CharField(max_length=10, choices=LightLevel.choices, null=True, blank=True)
+ care_level = models.CharField(max_length=10, choices=CareLevel.choices, null=True, blank=True)
+
+ def __str__(self):
+ return self.name
+
+
+class Comment(models.Model):
+ plant = models.ForeignKey(Plant, on_delete=models.CASCADE, related_name='comments')
+ user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
+ name = models.CharField(max_length=100)
+ content = models.TextField()
+ created_at = models.DateTimeField(auto_now_add=True)
+
+ def __str__(self):
+ return f"{self.name} - {self.plant.name}"
+
+
diff --git a/Planteer/plants/templates/plants/all.html b/Planteer/plants/templates/plants/all.html
new file mode 100644
index 0000000..381e158
--- /dev/null
+++ b/Planteer/plants/templates/plants/all.html
@@ -0,0 +1,116 @@
+{% extends 'Planteer/base.html' %}
+{% block content %}
+
+
+
+
+
+
Home
+
›
+
All Plants
+
+
+
+
+
All Plants
+
Browse our complete plant database
+
+
+
+ + Add New Plant
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
CATEGORY
+
+
+ All Categories
+
+
+ {% for value, label in categories %}
+
+ {{ label }}
+
+ {% endfor %}
+
+
+
+
+
EDIBILITY
+
+
+ Edible Only
+
+
+
+
+
+
+
COUNTRY
+
+
+
+
+
+
+
+
+
+
+
+
+ Search
+
+
+
+
+ Showing {{ plants|length }} plants
+
+
+
+ {% for plant in plants %}
+
+
+ {% include "plants/partials/plant_card.html" with plant=plant %}
+
+
+ {% empty %}
+
No plants found
+ {% endfor %}
+
+
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/Planteer/plants/templates/plants/country_plants.html b/Planteer/plants/templates/plants/country_plants.html
new file mode 100644
index 0000000..6e463b5
--- /dev/null
+++ b/Planteer/plants/templates/plants/country_plants.html
@@ -0,0 +1,38 @@
+{% extends 'Planteer/base.html' %}
+{% block content %}
+
+
+
+
+
+
+
+ {% if country.flag %}
+
+ {% endif %}
+
+
+
Plants native to {{ country.name }}
+
Showing {{ plants|length }} plants
+
+
+
+
+ {% for plant in plants %}
+
+ {% include "plants/partials/plant_card.html" with plant=plant %}
+
+ {% empty %}
+
No plants found for this country.
+ {% endfor %}
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/Planteer/plants/templates/plants/detail.html b/Planteer/plants/templates/plants/detail.html
new file mode 100644
index 0000000..70e882e
--- /dev/null
+++ b/Planteer/plants/templates/plants/detail.html
@@ -0,0 +1,164 @@
+{% extends 'Planteer/base.html' %}
+{% block content %}
+
+
+
+
+
+
+
+
+
+
+
+ {% if plant.image %}
+
+ {% endif %}
+
+
+
+
+
+
+
{{ plant.name }}
+
+
+ {{ plant.get_category_display }}
+ {% if plant.is_edible %}
+ Edible
+ {% endif %}
+
+
+
+
+
+
About
+
{{ plant.about }}
+
+
+
+
Used For
+
{{ plant.used_for }}
+
+
+
+ {% if plant.countries.all %}
+
+ {% endif %}
+
+
+ {% if request.user.is_staff %}
+
+ Edit Plant
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Comments ({{ comments|length }})
+
+ {% for comment in comments %}
+
+
+
{{ comment.content }}
+
+ {% empty %}
+
No comments yet. Be the first! 🌿
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+{% if related_plants %}
+
+
+
Related Plants
+
+ {% for related in related_plants %}
+
+
+ {% if related.image %}
+
+ {% endif %}
+
+
{{ related.name }}
+
{{ related.about|truncatechars:60 }}
+
View Details →
+
+
+
+ {% endfor %}
+
+
+{% endif %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/Planteer/plants/templates/plants/new.html b/Planteer/plants/templates/plants/new.html
new file mode 100644
index 0000000..b6ef73b
--- /dev/null
+++ b/Planteer/plants/templates/plants/new.html
@@ -0,0 +1,111 @@
+{% extends 'Planteer/base.html' %}
+{% load static %}
+{% block content %}
+
+
+
+
+
+
+
+
+
NEW PLANT
+
Add New Plant
+
Fill in the details below to add a new plant
+
+
+
+
+
+
+ {% csrf_token %}
+
+
+
+
Plant Name *
+
+ {% if form.name.errors %}
+
{{ form.name.errors.0 }}
+ {% endif %}
+
+
+
+
+
About *
+
{{ form.about.value|default:'' }}
+ {% if form.about.errors %}
+
{{ form.about.errors.0 }}
+ {% endif %}
+
+
+
+
+
Used For *
+
{{ form.used_for.value|default:'' }}
+ {% if form.used_for.errors %}
+
{{ form.used_for.errors.0 }}
+ {% endif %}
+
+
+
+
+
Image
+
+ {% if form.image.errors %}
+
{{ form.image.errors.0 }}
+ {% endif %}
+
+
+
+
+
+
Category *
+
+ -- Select Category --
+ Tropical
+ Succulents
+ Herbs
+ Ferns
+ Flowering
+ Trees
+
+ {% if form.category.errors %}
+
{{ form.category.errors.0 }}
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/Planteer/plants/templates/plants/partials/plant_card.html b/Planteer/plants/templates/plants/partials/plant_card.html
new file mode 100644
index 0000000..476d13a
--- /dev/null
+++ b/Planteer/plants/templates/plants/partials/plant_card.html
@@ -0,0 +1,63 @@
+
+
+
+
+ {{ plant.get_category_display }}
+ {% if plant.is_edible %}
+ Edible
+ {% endif %}
+
+
+
+ {% with favs=request.COOKIES.favs|default:'' %}
+
+
+
+ {% endwith %}
+
+
+ {% if plant.image %}
+
+ {% endif %}
+
+
+
+
{{ plant.name }}
+
{{ plant.about|truncatechars:70 }}
+
+
+ {% if plant.water or plant.light or plant.countries.first %}
+
+ {% if plant.water %}
+ {{ plant.get_water_display }}
+ {% endif %}
+ {% if plant.light %}
+ {{ plant.get_light_display }}
+ {% endif %}
+ {% if plant.countries.first %}
+ {{ plant.countries.first.name }}
+ {% endif %}
+
+ {% endif %}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Planteer/plants/templates/plants/partials/plant_card_simple.html b/Planteer/plants/templates/plants/partials/plant_card_simple.html
new file mode 100644
index 0000000..514f1e5
--- /dev/null
+++ b/Planteer/plants/templates/plants/partials/plant_card_simple.html
@@ -0,0 +1,10 @@
+
+ {% if plant.image %}
+
+ {% endif %}
+
+
{{ plant.name }}
+
{{ plant.about|truncatechars:50 }}
+
{{ plant.get_category_display }}
+
+
\ No newline at end of file
diff --git a/Planteer/plants/templates/plants/search.html b/Planteer/plants/templates/plants/search.html
new file mode 100644
index 0000000..e69de29
diff --git a/Planteer/plants/templates/plants/update.html b/Planteer/plants/templates/plants/update.html
new file mode 100644
index 0000000..433ae4e
--- /dev/null
+++ b/Planteer/plants/templates/plants/update.html
@@ -0,0 +1,45 @@
+{% extends 'Planteer/base.html' %}
+{% block content %}
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/Planteer/plants/tests.py b/Planteer/plants/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/Planteer/plants/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/Planteer/plants/urls.py b/Planteer/plants/urls.py
new file mode 100644
index 0000000..cac0c7f
--- /dev/null
+++ b/Planteer/plants/urls.py
@@ -0,0 +1,24 @@
+from django.urls import path
+from . import views
+
+app_name = "plants"
+
+urlpatterns = [
+ path('all/', views.all_plants_view, name='all_plants'),
+
+ path('
/detail/', views.plant_detail_view, name='plant_detail'),
+
+ path('new/', views.create_plant_view, name='create_plant'),
+
+ path('/update/', views.update_plant_view, name='update_plant'),
+
+ path('/delete/', views.delete_plant_view, name='delete_plant'),
+
+ path('search/', views.search_view, name='search'),
+
+ path('/country/', views.country_plants_view, name='country_plants'),
+
+ path('/favourite/', views.toggle_favourite, name='toggle_favourite'),
+
+
+]
\ No newline at end of file
diff --git a/Planteer/plants/views.py b/Planteer/plants/views.py
new file mode 100644
index 0000000..c3cf56e
--- /dev/null
+++ b/Planteer/plants/views.py
@@ -0,0 +1,190 @@
+from django.shortcuts import render, redirect, get_object_or_404
+from .models import Plant, Comment, Country
+from .forms import PlantForm
+
+
+def all_plants_view(request):
+ plants = Plant.objects.all()
+
+ category = request.GET.get('category')
+ edible = request.GET.get('edible')
+ country = request.GET.get('country')
+
+ if category:
+ plants = plants.filter(category=category)
+
+ if edible == 'true':
+ plants = plants.filter(is_edible=True)
+
+ if country:
+ plants = plants.filter(countries__id=country)
+
+ countries = Country.objects.all()
+
+ return render(request, 'plants/all.html', {
+ 'plants': plants,
+ 'categories': Plant.Category.choices,
+ 'selected_category': category,
+ 'selected_edible': edible,
+ 'selected_country': country,
+ 'countries': countries,
+ })
+
+
+def plant_detail_view(request, id):
+ plant = get_object_or_404(Plant, id=id)
+ comments = plant.comments.all().order_by('-created_at')
+ related_plants = Plant.objects.filter(category=plant.category).exclude(id=plant.id)[:3]
+
+ if request.method == 'POST':
+ if request.user.is_authenticated:
+ Comment.objects.create(
+ plant=plant,
+ user=request.user,
+ name=request.user.username,
+ content=request.POST.get('content'),
+ )
+ return redirect(f'/plants/{id}/detail/')
+
+ return render(request, 'plants/detail.html', {
+ 'plant': plant,
+ 'comments': comments,
+ 'related_plants': related_plants,
+ })
+
+
+def create_plant_view(request):
+ if not request.user.is_staff:
+ return redirect('/')
+
+ if request.method == 'POST':
+ name = request.POST.get('name')
+ about = request.POST.get('about')
+ used_for = request.POST.get('used_for')
+ image = request.FILES.get('image')
+ category = request.POST.get('category')
+ is_edible = request.POST.get('is_edible') == 'on'
+
+ Plant.objects.create(
+ name=name,
+ about=about,
+ used_for=used_for,
+ image=image,
+ category=category,
+ is_edible=is_edible
+ )
+
+ return redirect('/plants/all/')
+
+ return render(request, 'plants/new.html')
+
+
+def update_plant_view(request, id):
+ if not request.user.is_staff:
+ return redirect('/')
+
+ plant = get_object_or_404(Plant, id=id)
+
+ if request.method == 'POST':
+ plant.name = request.POST.get('name')
+ plant.about = request.POST.get('about')
+ plant.used_for = request.POST.get('used_for')
+
+ image = request.FILES.get('image')
+ if image:
+ plant.image = image
+
+ category = request.POST.get('category')
+ if not category:
+ category = plant.category
+
+ plant.category = category
+ plant.is_edible = request.POST.get('is_edible') == 'on'
+ plant.save()
+ return redirect('/plants/all/')
+
+ return render(request, 'plants/update.html', {'plant': plant})
+
+
+def delete_plant_view(request, id):
+ if not request.user.is_staff:
+ return redirect('/')
+
+ plant = get_object_or_404(Plant, id=id)
+ plant.delete()
+ return redirect('/plants/all/')
+
+
+def search_view(request):
+ query = request.GET.get('q', '').strip()
+ category = request.GET.get('category')
+ edible = request.GET.get('edible')
+
+ plants = Plant.objects.all()
+
+ category_match = None
+ for value, label in Plant.Category.choices:
+ if query.lower() == label.lower():
+ category_match = value
+ break
+
+ if category_match:
+ plants = plants.filter(category=category_match)
+ category = category_match
+ elif query:
+ plants = plants.filter(name__icontains=query)
+
+ if category and not category_match:
+ plants = plants.filter(category=category)
+
+ if edible == 'true':
+ plants = plants.filter(is_edible=True)
+
+ return render(request, 'plants/all.html', {
+ 'plants': plants,
+ 'categories': Plant.Category.choices,
+ 'selected_category': category,
+ 'selected_edible': edible
+ })
+
+
+def add_plant(request):
+ if not request.user.is_staff:
+ return redirect('/')
+
+ if request.method == 'POST':
+ form = PlantForm(request.POST, request.FILES)
+ if form.is_valid():
+ form.save()
+ return redirect('all_plants')
+ else:
+ form = PlantForm()
+
+ return render(request, 'plants/new.html', {'form': form})
+
+
+def country_plants_view(request, id):
+ country = get_object_or_404(Country, id=id)
+ plants = Plant.objects.filter(countries__id=id)
+
+ return render(request, 'plants/country_plants.html', {
+ 'country': country,
+ 'plants': plants,
+ })
+
+
+def toggle_favourite(request, id):
+ get_object_or_404(Plant, id=id)
+ redirect_url = request.GET.get('next', '/plants/all/')
+ response = redirect(redirect_url)
+
+ favs = request.COOKIES.get('favs', '')
+ fav_list = [f for f in favs.split(',') if f]
+
+ if str(id) in fav_list:
+ fav_list.remove(str(id))
+ else:
+ fav_list.append(str(id))
+
+ response.set_cookie('favs', ','.join(fav_list), max_age=60*60*24*30)
+ return response
\ No newline at end of file
diff --git a/Planteer/static/Planteer/css/style.css b/Planteer/static/Planteer/css/style.css
new file mode 100644
index 0000000..318d9dc
--- /dev/null
+++ b/Planteer/static/Planteer/css/style.css
@@ -0,0 +1,1841 @@
+/* ================= DARK MODE ================= */
+.dark-mode {
+ background-color: #0f1f0f !important;
+ color: #e0e0e0;
+}
+
+.dark-mode .navbar {
+ background-color: #0f1f0f !important;
+}
+
+.dark-mode .nav-logo-icon {
+ background: #1e5c1e;
+}
+
+.dark-mode .nav-link,
+.dark-mode .nav-linkc {
+ color: #a0c0a0 !important;
+}
+
+.dark-mode .brand-color {
+ color: #4a9a4a !important;
+}
+
+.dark-mode .btn-add {
+ background: #1e5c1e;
+ color: white;
+}
+
+.dark-mode .plant-card {
+ background-color: #162816 !important;
+ border-color: #1e3d1e !important;
+ box-shadow: 0 2px 12px rgba(0,0,0,0.3);
+}
+
+.dark-mode .card-body,
+.dark-mode .plant-info {
+ background-color: #162816 !important;
+ border-top-color: #1e3d1e !important;
+ color: #e0e0e0 !important;
+}
+
+.dark-mode .card-body h5,
+.dark-mode .plant-info h5 {
+ color: #ffffff !important;
+}
+
+.dark-mode .card-body p,
+.dark-mode .plant-info p {
+ color: #8ab08a !important;
+}
+
+.dark-mode .plant-meta {
+ color: #6a8a6a !important;
+ border-bottom-color: #1e3d1e !important;
+}
+
+.dark-mode .filter-box {
+ background-color: #162816 !important;
+ border: 1px solid #1e3d1e;
+}
+
+.dark-mode .filter-item {
+ color: #c0d8c0 !important;
+}
+
+.dark-mode .filter-item:hover {
+ background: #1e3d1e !important;
+}
+
+.dark-mode .filter-item.active {
+ background: #2e5d34 !important;
+ color: white !important;
+}
+
+.dark-mode .plant-form,
+.dark-mode .contact-form-box,
+.dark-mode .comment-form-box,
+.dark-mode .delete-box {
+ background-color: #162816 !important;
+ border-color: #1e3d1e !important;
+ color: #e0e0e0 !important;
+}
+
+.dark-mode .form-control {
+ background-color: #1a3a1a !important;
+ border-color: #2a502a !important;
+ color: #e0e0e0 !important;
+}
+
+.dark-mode .form-control::placeholder {
+ color: #6a8a6a !important;
+}
+
+.dark-mode .msg-card {
+ background-color: #162816 !important;
+ border-color: #1e3d1e !important;
+}
+
+.dark-mode .msg-name {
+ color: #ffffff !important;
+}
+
+.dark-mode .msg-body {
+ color: #8ab08a !important;
+ border-top-color: #1e3d1e !important;
+}
+
+.dark-mode .detail-info-box {
+ background-color: #162816 !important;
+ border-color: #1e3d1e !important;
+}
+
+.dark-mode .detail-name,
+.dark-mode .page-title {
+ color: #ffffff !important;
+}
+
+.dark-mode .detail-text,
+.dark-mode .page-subtitle,
+.dark-mode .showing-text {
+ color: #8ab08a !important;
+}
+
+.dark-mode .why-section {
+ background-color: #0a150a !important;
+}
+
+.dark-mode .why-card {
+ background-color: #162816 !important;
+ color: #e0e0e0 !important;
+}
+
+.dark-mode .categories {
+ background-color: #0f1f0f !important;
+ background-image: radial-gradient(#1e3d1e 1px, transparent 1px) !important;
+}
+
+.dark-mode .hero-search {
+ background: #162816 !important;
+ border-color: #2e5d34 !important;
+}
+
+.dark-mode .hero-search input {
+ background: #162816 !important;
+ color: #e0e0e0 !important;
+}
+
+.dark-mode .hero-title {
+ color: #4a9a4a !important;
+}
+
+.dark-mode .footer-pro {
+ background-color: #0a150a !important;
+ border-top: 1px solid #1a3a1a;
+}
+
+.dark-mode .footer-text,
+.dark-mode .footer-links a,
+.dark-mode .footer-bottom {
+ color: #8ab08a !important;
+}
+
+.dark-mode hr {
+ border-color: #1e3d1e !important;
+}
+
+.dark-mode .recent-section {
+ background: #0a150a;
+}
+
+.dark-mode .recent-title {
+ color: #ffffff;
+}
+
+.dark-mode .btn-view-all {
+ background: #162816;
+ color: #ffffff;
+ border-color: #1e3d1e;
+}
+
+.dark-mode .btn-view-all:hover {
+ background: #1e3d1e;
+}
+
+.dark-mode .quick-tips-section {
+ background: #0a150a;
+}
+
+.dark-mode .care-guides-section {
+ background: #0f1f0f;
+}
+
+.dark-mode .care-card {
+ background: #162816;
+ border-color: #1e3d1e;
+}
+
+.dark-mode .section-title,
+.dark-mode .care-card h5 {
+ color: #ffffff;
+}
+
+.dark-mode .care-card p,
+.dark-mode .care-list,
+.dark-mode .quick-tip p {
+ color: #8ab08a;
+}
+
+.dark-mode .articles-section {
+ background: #0a150a;
+}
+
+.dark-mode .article-card {
+ background: #162816;
+ border-color: #1e3d1e;
+}
+
+.dark-mode .article-body h5 {
+ color: #ffffff;
+}
+
+.dark-mode .article-body p {
+ color: #8ab08a;
+}
+
+.dark-mode .article-meta {
+ color: #6a8a6a;
+}
+
+.dark-mode .how-section,
+.dark-mode .quiz-section {
+ background: #0f1f0f;
+}
+
+.dark-mode .quiz-q {
+ color: #ffffff;
+}
+
+.dark-mode .quiz-option-box {
+ border-color: #1e3d1e;
+ background: #162816;
+ color: #e0e0e0;
+}
+
+.dark-mode .quiz-option input:checked + .quiz-option-box {
+ border-color: #4a9a4a;
+ background: #1e3d1e;
+}
+
+.dark-mode .navbar.scrolled {
+ border-bottom: 1px solid #2a502a;
+}
+
+/* ================= GLOBAL ================= */
+body {
+ padding-top: 80px;
+ font-family: sans-serif;
+}
+
+a {
+ text-decoration: none;
+}
+
+/* ================= NAVBAR ================= */
+.navbar {
+ background-color: #0f1f0f !important;
+ border-bottom: 1px solid transparent;
+ transition: border-bottom 0.3s ease;
+ padding: 14px 0;
+}
+
+.navbar.scrolled {
+ border-bottom: 1px solid #1e3d1e;
+}
+
+.nav-logo-icon {
+ width: 36px;
+ height: 36px;
+ background: #2e5d34;
+ border-radius: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: white;
+ font-size: 16px;
+}
+
+.brand-color {
+ color: #ffffff !important;
+ font-size: 20px;
+}
+
+.nav-link {
+ color: #ffffff !important;
+ font-size: 15px;
+ font-weight: 500;
+ transition: 0.2s;
+ padding: 0 !important;
+ line-height: normal !important;
+ display: flex !important;
+ align-items: center !important;
+}
+
+.nav-divider {
+ width: 1px;
+ height: 24px;
+ background: rgba(255,255,255,0.15);
+ margin: 0 4px;
+}
+
+.nav-link:hover {
+ color: #4a9a4a !important;
+}
+
+.active-link {
+ color: #4a9a4a !important;
+ font-weight: 600;
+}
+
+.nav-linkc {
+ color: #ffffff;
+ font-weight: 500;
+ text-decoration: none;
+ font-size: 15px;
+}
+
+.nav-linkc:hover {
+ color: #4a9a4a;
+}
+
+.btn-dark-toggle {
+ width: 40px;
+ height: 40px;
+ border-radius: 10px;
+ background: #2a3a2a;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #ffffff;
+ font-size: 16px;
+ text-decoration: none;
+ transition: 0.2s;
+}
+
+.btn-dark-toggle:hover {
+ background: #3a4a3a;
+ color: white;
+}
+
+.dark-mode .btn-dark-toggle {
+ color: #a0c0a0;
+}
+
+.btn-add {
+ background: #2e5d34;
+ color: white;
+ border: none;
+ padding: 10px 20px;
+ border-radius: 10px;
+ font-weight: 600;
+ font-size: 14px;
+ text-decoration: none;
+ transition: 0.2s;
+ margin-left: 8px;
+}
+
+.btn-add:hover {
+ background: #2e7d2e;
+ color: white;
+}
+
+/* ================= HERO ================= */
+.hero {
+ position: relative;
+ height: 100vh;
+ background: url("https://res.cloudinary.com/dd2nfo3ua/image/upload/v1776882537/bg_planterr_x5onai.avif") center/cover no-repeat;
+ display: flex;
+ align-items: center;
+ color: white;
+ overflow: hidden;
+}
+
+.hero-overlay {
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(
+ 90deg,
+ rgba(0, 20, 10, 0.95) 0%,
+ rgba(0, 20, 10, 0.75) 40%,
+ rgba(0, 0, 0, 0.2) 100%
+ );
+}
+
+.hero-content {
+ position: relative;
+ z-index: 2;
+ max-width: 600px;
+}
+
+.hero-badge {
+ background: rgba(155,226,140,0.15);
+ border: 1px solid rgba(155,226,140,0.3);
+ padding: 6px 14px;
+ border-radius: 20px;
+ font-size: 13px;
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ margin-bottom: 15px;
+}
+
+.hero-title {
+ font-size: 70px;
+ font-weight: 800;
+ line-height: 1.1;
+}
+
+.hero-title span {
+ color: #9be28c;
+}
+
+.hero-desc {
+ margin-top: 15px;
+ opacity: 0.8;
+ max-width: 500px;
+}
+
+.hero-search {
+ display: flex;
+ align-items: center;
+ margin-top: 25px;
+ background: rgba(255,255,255,0.08);
+ border: 1px solid rgba(155,226,140,0.2);
+ border-radius: 12px;
+ overflow: hidden;
+ max-width: 420px;
+}
+
+.hero-search i {
+ padding: 0 12px;
+ color: #9be28c;
+}
+
+.hero-search input {
+ flex: 1;
+ padding: 12px;
+ background: transparent;
+ border: none;
+ color: white;
+ outline: none;
+}
+
+.hero-search button {
+ background: #9be28c;
+ border: none;
+ padding: 12px 20px;
+ color: black;
+ font-weight: bold;
+ cursor: pointer;
+}
+
+.hero-tags {
+ margin-top: 15px;
+}
+
+.hero-tags span {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ margin: 8px 6px 0 0;
+ padding: 6px 14px;
+ border-radius: 20px;
+ background: rgba(255,255,255,0.1);
+ font-size: 13px;
+}
+
+.hero-stats {
+ margin-top: 50px;
+ display: flex;
+ justify-content: space-between;
+ background: rgba(0, 30, 10, 0.5);
+ border: 1px solid rgba(155,226,140,0.15);
+ border-radius: 16px;
+ backdrop-filter: blur(15px);
+ overflow: hidden;
+ transform: translateY(30px);
+}
+
+.stat-item {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ padding: 20px 25px;
+ position: relative;
+}
+
+.stat-item:not(:last-child) {
+ border-right: 1px solid rgba(255,255,255,0.08);
+}
+
+.stat-icon {
+ width: 45px;
+ height: 45px;
+ background: rgba(155,226,140,0.15);
+ border-radius: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.stat-icon i {
+ color: #9be28c;
+ font-size: 18px;
+}
+
+.stat-item h4 {
+ margin: 0;
+ font-size: 20px;
+ font-weight: bold;
+}
+
+.stat-item small {
+ display: block;
+ opacity: 0.7;
+ font-size: 13px;
+}
+
+/* ================= CATEGORIES ================= */
+.categories {
+ margin-top: 80px;
+ padding: 60px 0;
+ background-color: #f3efe8;
+ background-image: radial-gradient(#c8b99a 1px, transparent 1px);
+ background-size: 20px 20px;
+}
+
+.cat-subtitle {
+ color: #9aa89a;
+ font-size: 12px;
+ letter-spacing: 2px;
+}
+
+.cat-title {
+ font-size: 36px;
+ font-weight: 700;
+ color: #2e5d34;
+}
+
+.cat-card {
+ display: block;
+ position: relative;
+ border-radius: 20px;
+ overflow: hidden;
+ cursor: pointer;
+ text-decoration: none;
+ color: inherit;
+}
+
+.cat-card img {
+ width: 100%;
+ height: 150px;
+ object-fit: cover;
+ transition: 0.3s;
+}
+
+.cat-card span {
+ position: absolute;
+ bottom: 10px;
+ left: 15px;
+ color: white;
+ font-weight: 600;
+ font-size: 14px;
+ z-index: 3;
+}
+
+.cat-card::after {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(to top, rgba(0,0,0,0.5), transparent);
+}
+
+.cat-card:hover img {
+ transform: scale(1.05);
+}
+
+/* ================= PLANTS SECTION ================= */
+.plants-section {
+ margin-top: 80px;
+ margin-bottom: 80px;
+}
+
+.plants-title {
+ font-weight: 700;
+ color: #2e5d34;
+}
+
+.plants-subtitle {
+ color: #777;
+ font-size: 14px;
+}
+
+.more-link {
+ text-decoration: none;
+ color: #2e5d34;
+ font-weight: 600;
+}
+
+/* ================= PLANT CARD ================= */
+.plant-card {
+ background: white;
+ border-radius: 20px;
+ overflow: hidden;
+ position: relative;
+ transition: all 0.3s ease;
+ border: 1px solid #f0f0f0;
+ box-shadow: 0 2px 12px rgba(0,0,0,0.06);
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+.plant-card:hover {
+ transform: translateY(-6px);
+ box-shadow: 0 16px 40px rgba(0,0,0,0.12);
+}
+
+.plant-card img {
+ width: 100%;
+ height: 220px;
+ object-fit: cover;
+ transition: 0.4s;
+ flex-shrink: 0;
+}
+
+.plant-card:hover img {
+ transform: scale(1.04);
+}
+
+.plant-category {
+ font-size: 13px;
+ color: #2e5d34;
+ font-weight: 600;
+}
+
+/* ================= PLANT INFO (Home Cards) ================= */
+.plant-info {
+ padding: 16px;
+ background: white;
+ border-top: 1px solid #f0f0f0;
+}
+
+.plant-info h5 {
+ font-weight: 600;
+ margin-bottom: 5px;
+}
+
+.plant-info p {
+ color: #777;
+ font-size: 13px;
+ margin-bottom: 6px;
+}
+
+/* ================= RECENT SECTION ================= */
+.recent-section {
+ padding: 80px 0;
+ background: #ade7a243;
+}
+
+.recent-title {
+ font-size: 36px;
+ font-weight: 700;
+ color: #1c1c1c;
+}
+
+.btn-view-all {
+ background: white;
+ color: #1c1c1c;
+ border: 1px solid #e0e0e0;
+ padding: 10px 20px;
+ border-radius: 10px;
+ font-weight: 600;
+ text-decoration: none;
+ transition: 0.2s;
+}
+
+.btn-view-all:hover {
+ background: #f0f0f0;
+ color: #1c1c1c;
+}
+
+/* ================= BADGES ================= */
+.card-badges {
+ position: absolute;
+ top: 12px;
+ left: 12px;
+ display: flex;
+ gap: 6px;
+ z-index: 2;
+}
+
+.badge-category {
+ background: rgba(46, 93, 52, 0.9);
+ color: white;
+ padding: 4px 10px;
+ border-radius: 20px;
+ font-size: 11px;
+ backdrop-filter: blur(4px);
+}
+
+.badge-edible {
+ background: rgba(243, 156, 18, 0.9);
+ color: white;
+ padding: 4px 10px;
+ border-radius: 20px;
+ font-size: 11px;
+ backdrop-filter: blur(4px);
+}
+
+/* ================= CARD BODY ================= */
+.card-body {
+ padding: 16px;
+ background: white;
+ border-top: 1px solid #f0f0f0;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+}
+
+.card-body h5 {
+ font-weight: 700;
+ margin-bottom: 6px;
+ color: #1c1c1c;
+}
+
+.card-body p {
+ color: #777;
+ font-size: 13px;
+ margin-bottom: 10px;
+ line-height: 1.6;
+}
+
+/* ================= PLANT META ================= */
+.plant-meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ font-size: 12px;
+ color: #888;
+ margin-bottom: 12px;
+ padding-bottom: 12px;
+ border-bottom: 1px solid #f5f5f5;
+}
+
+.plant-meta i {
+ color: #2e5d34;
+ margin-right: 3px;
+}
+
+/* ================= PLANT FOOTER ================= */
+.plant-footer {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: auto;
+}
+
+.details-link {
+ color: #2e5d34;
+ font-weight: 500;
+ text-decoration: none;
+}
+
+.details-link:hover::after {
+ margin-left: 5px;
+}
+
+.card-actions a {
+ margin-left: 13px;
+}
+
+.card-actions i {
+ color: #ccc;
+ cursor: pointer;
+ transition: 0.3s;
+}
+
+.card-actions i:hover {
+ color: #2e5d34;
+ transform: scale(1.1);
+}
+
+/* ================= CARE BADGE ================= */
+.care-badge {
+ position: absolute;
+ bottom: 12px;
+ right: 12px;
+ padding: 4px 12px;
+ border-radius: 20px;
+ font-size: 12px;
+ font-weight: 600;
+ z-index: 2;
+}
+
+.care-easy {
+ background: rgba(255,255,255,0.85);
+ color: #2e7d32;
+}
+
+.care-med {
+ background: rgba(255,255,255,0.85);
+ color: #e65100;
+}
+
+.care-hard {
+ background: rgba(255,255,255,0.85);
+ color: #c62828;
+}
+
+/* ================= FAV BUTTON ================= */
+.fav-btn {
+ position: absolute;
+ top: 12px;
+ right: 12px;
+ width: 36px;
+ height: 36px;
+ border-radius: 50%;
+ background: rgba(255,255,255,0.9);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ z-index: 3;
+ transition: 0.2s;
+ font-size: 15px;
+ color: #aaa;
+ text-decoration: none;
+}
+
+.fav-btn:hover {
+ background: white;
+ color: #e74c3c;
+}
+
+.fav-btn.fav-active {
+ color: #e74c3c;
+}
+
+/* ================= FAVOURITES ================= */
+.fav-empty-icon {
+ font-size: 70px;
+ color: #e74c3c;
+ opacity: 0.4;
+}
+
+/* ================= WHY SECTION ================= */
+.why-section {
+ margin-top: 80px;
+ padding: 60px 0;
+ background: #f3efe8;
+}
+
+.why-subtitle {
+ font-size: 12px;
+ letter-spacing: 2px;
+ color: #9aa89a;
+}
+
+.why-title {
+ font-size: 36px;
+ font-weight: 700;
+ color: #2e5d34;
+}
+
+.why-card {
+ background: white;
+ padding: 30px 20px;
+ border-radius: 20px;
+ text-align: center;
+ transition: 0.3s;
+}
+
+.why-card:hover {
+ transform: translateY(-5px);
+}
+
+.icon-box {
+ width: 60px;
+ height: 60px;
+ background: #e6f4ea;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin: 0 auto 15px;
+ font-size: 20px;
+ color: #2e5d34;
+}
+
+/* ================= TESTIMONIALS ================= */
+.testimonials {
+ background: #234d20;
+ padding: 80px 0;
+ color: white;
+ margin-top: 80px;
+}
+
+.test-subtitle {
+ font-size: 12px;
+ letter-spacing: 2px;
+ opacity: 0.7;
+}
+
+.test-title {
+ font-size: 36px;
+ font-weight: 700;
+}
+
+.test-card {
+ background: rgba(255,255,255,0.1);
+ padding: 30px;
+ border-radius: 20px;
+}
+
+.test-text {
+ margin-bottom: 20px;
+ line-height: 1.6;
+}
+
+#testCarousel {
+ position: relative;
+}
+
+.carousel-control-prev,
+.carousel-control-next {
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ width: auto;
+ height: auto;
+}
+
+.carousel-control-prev {
+ left: -60px;
+}
+
+.carousel-control-next {
+ right: -60px;
+}
+
+.user {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.user-icon {
+ width: 45px;
+ height: 45px;
+ border-radius: 50%;
+ background: rgba(255,255,255,0.2);
+ color: #234d20;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 18px;
+}
+
+.user h6 {
+ margin: 0;
+ font-size: 15px;
+}
+
+.user small {
+ color: #ccc;
+}
+
+/* ================= BREADCRUMB & PAGE HEADER ================= */
+.breadcrumb-link {
+ color: #9aa89a;
+ font-size: 17px;
+ text-decoration: none;
+}
+
+.breadcrumb-link:hover {
+ color: #2e5d34;
+}
+
+.breadcrumb-green {
+ color: #2e5d34;
+ font-size: 17px;
+ text-decoration: none;
+}
+
+.breadcrumb-green:hover {
+ color: #24492a;
+}
+
+.breadcrumb-current {
+ color: #2e5d34;
+ font-weight: 500;
+}
+
+.page-title {
+ font-size: 40px;
+ font-weight: 700;
+ color: #1c1c1c;
+ letter-spacing: -0.5px;
+}
+
+.page-subtitle {
+ color: #8c8c8c;
+ font-size: 15px;
+ margin-top: 5px;
+}
+
+/* ================= BUTTONS ================= */
+.btn-main {
+ background: #2e5d34;
+ color: white;
+ border: none;
+ border-radius: 12px;
+ padding: 12px 20px;
+ font-weight: 600;
+ transition: 0.2s;
+}
+
+.btn-main:hover {
+ background: #24492a;
+ color: white;
+}
+
+.btn-light {
+ background: #f3efe8;
+ border: 1px solid #e8ddd0;
+ border-radius: 12px;
+ padding: 12px;
+ text-align: center;
+ color: #5a4a38;
+ transition: 0.2s;
+}
+
+.btn-light:hover {
+ background: #e8ddd0;
+ color: #3d2e1e;
+}
+
+/* ================= FILTER BOX ================= */
+.filter-box {
+ background: #f6f7f6;
+ padding: 20px;
+ border-radius: 20px;
+}
+
+.filter-title {
+ font-size: 12px;
+ color: #9aa89a;
+ letter-spacing: 1px;
+ margin-bottom: 10px;
+}
+
+.filter-item {
+ display: block;
+ padding: 10px 12px;
+ border-radius: 10px;
+ color: #333;
+ margin-bottom: 6px;
+ text-decoration: none;
+}
+
+.filter-item:hover {
+ background: #e6f4ea;
+ color: #2e5d34;
+}
+
+.filter-item.active {
+ background: #2e5d34;
+ color: white;
+}
+
+.clear-link {
+ font-size: 12px;
+ color: #e74c3c;
+}
+
+.showing-text {
+ color: #666;
+ margin-bottom: 15px;
+}
+
+.country-list {
+ max-height: 180px;
+ overflow-y: auto;
+ scrollbar-width: thin;
+ scrollbar-color: #2e5d34 #f0f0f0;
+}
+
+/* ================= FORM ================= */
+.plant-form {
+ background: #fdf8f3;
+ border: 1px solid #e8ddd0;
+ padding: 40px;
+ border-radius: 24px;
+}
+
+.plant-form label {
+ font-weight: 600;
+ margin-bottom: 6px;
+ display: block;
+ color: #2e5d34;
+}
+
+.form-control {
+ border-radius: 14px;
+ padding: 12px;
+ border: 1px solid #e0d8cc;
+ background: #ffffff;
+ transition: 0.2s;
+}
+
+.form-control:focus {
+ border-color: #2e5d34;
+ box-shadow: 0 0 0 2px rgba(46, 93, 52, 0.1);
+}
+
+textarea.form-control {
+ resize: none;
+}
+
+select.form-control {
+ height: 45px;
+}
+
+.form-check-input {
+ width: 45px;
+ height: 22px;
+ cursor: pointer;
+}
+
+.form-check-input:checked {
+ background-color: #2e5d34;
+ border-color: #2e5d34;
+}
+
+.d-flex.gap-3 {
+ margin-top: 20px;
+}
+
+/* ================= DETAIL PAGE ================= */
+.detail-img {
+ width: 100%;
+ border-radius: 20px;
+ object-fit: cover;
+ max-height: 420px;
+}
+
+.detail-info-box {
+ background: #fdf8f3;
+ border: 1px solid #e8ddd0;
+ border-radius: 20px;
+ padding: 30px;
+ height: 100%;
+}
+
+.detail-name {
+ font-size: 32px;
+ font-weight: 700;
+ color: #1c1c1c;
+ margin-bottom: 12px;
+}
+
+.detail-label {
+ font-weight: 700;
+ color: #2e5d34;
+ margin-bottom: 6px;
+}
+
+.detail-text {
+ color: #5a4a38;
+ font-size: 15px;
+ line-height: 1.7;
+}
+
+.detail-section {
+ margin-bottom: 20px;
+}
+
+.comment-form-box {
+ background: #fdf8f3;
+ border: 1px solid #e8ddd0;
+ border-radius: 20px;
+ padding: 24px;
+}
+
+.comment-form-box label {
+ font-weight: 600;
+ color: #2e5d34;
+ margin-bottom: 6px;
+ display: block;
+}
+
+.country-badge {
+ background: #e6f4ea;
+ color: #2e5d34;
+ padding: 6px 14px;
+ border-radius: 20px;
+ font-size: 13px;
+ font-weight: 600;
+ text-decoration: none;
+ transition: 0.2s;
+}
+
+.country-badge:hover {
+ background: #2e5d34;
+ color: white;
+}
+
+/* ================= CONTACT ================= */
+.contact-form-box {
+ background: #fdf8f3;
+ border: 1px solid #e8ddd0;
+ border-radius: 24px;
+ padding: 40px;
+}
+
+.contact-form-box label {
+ font-weight: 600;
+ color: #2e5d34;
+ margin-bottom: 6px;
+ display: block;
+}
+
+.alert-success-custom {
+ background: #e6f4ea;
+ color: #2e5d34;
+ border-radius: 12px;
+ padding: 14px;
+ font-weight: 600;
+}
+
+/* ================= MESSAGES CARD ================= */
+.msg-card {
+ background-color: #fdf8f3;
+ border: 1px solid #e8ddd0;
+ border-radius: 16px;
+ padding: 24px;
+ transition: box-shadow 0.3s ease;
+}
+
+.msg-card:hover {
+ box-shadow: 0 6px 20px rgba(180, 150, 110, 0.15);
+}
+
+.msg-card-header {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ margin-bottom: 16px;
+}
+
+.msg-avatar {
+ width: 46px;
+ height: 46px;
+ border-radius: 50%;
+ background-color: #e8ddd0;
+ color: #7a6248;
+ font-weight: 700;
+ font-size: 15px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.msg-name {
+ margin: 0;
+ font-weight: 600;
+ color: #3d2e1e;
+ font-size: 15px;
+}
+
+.msg-email {
+ margin: 0;
+ font-size: 13px;
+ color: #a08060;
+}
+
+.msg-body {
+ color: #5a4a38;
+ font-size: 14px;
+ line-height: 1.7;
+ margin-bottom: 14px;
+ border-top: 1px solid #e8ddd0;
+ padding-top: 14px;
+}
+
+.msg-date {
+ font-size: 12px;
+ color: #b0967a;
+}
+
+/* ================= DELETE BOX ================= */
+.delete-box {
+ background: #fdf8f3;
+ border: 1px solid #e8ddd0;
+ border-radius: 24px;
+ padding: 40px;
+}
+
+.delete-icon {
+ font-size: 50px;
+}
+
+/* ================= CARE TIPS ================= */
+.care-hero {
+ background: linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.5)),
+ url('https://images.unsplash.com/photo-1545241047-6083a3684587?w=1600') center/cover;
+ padding: 120px 0;
+ color: white;
+}
+
+.care-hero-title {
+ font-size: 52px;
+ font-weight: 700;
+ color: white;
+}
+
+.care-hero-desc {
+ font-size: 18px;
+ opacity: 0.9;
+ max-width: 600px;
+ margin: 0 auto;
+}
+
+.care-tags span {
+ display: inline-block;
+ border: 1px solid rgba(255,255,255,0.5);
+ color: white;
+ padding: 8px 18px;
+ border-radius: 30px;
+ margin: 4px;
+ font-size: 14px;
+}
+
+.quick-tips-section {
+ padding: 60px 0;
+ background: #f9f9f7;
+}
+
+.quick-tip {
+ display: flex;
+ align-items: flex-start;
+ gap: 14px;
+}
+
+.quick-tip-icon {
+ font-size: 22px;
+ margin-top: 2px;
+ flex-shrink: 0;
+}
+
+.quick-tip p {
+ color: #555;
+ font-size: 14px;
+ line-height: 1.6;
+ margin: 0;
+}
+
+.care-guides-section {
+ padding: 80px 0;
+}
+
+.section-title {
+ font-size: 32px;
+ font-weight: 700;
+ color: #1c1c1c;
+}
+
+.care-card {
+ background: white;
+ border: 1px solid #f0f0f0;
+ border-radius: 20px;
+ padding: 28px;
+ height: 100%;
+ transition: 0.3s;
+}
+
+.care-card:hover {
+ box-shadow: 0 8px 24px rgba(0,0,0,0.08);
+ transform: translateY(-4px);
+}
+
+.care-card-icon {
+ width: 52px;
+ height: 52px;
+ border-radius: 14px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 22px;
+ margin-bottom: 16px;
+}
+
+.care-card h5 {
+ font-weight: 700;
+ margin-bottom: 8px;
+}
+
+.care-card p {
+ color: #777;
+ font-size: 14px;
+ margin-bottom: 16px;
+}
+
+.care-list {
+ padding-left: 16px;
+ color: #555;
+ font-size: 14px;
+ line-height: 2;
+}
+
+/* ================= ARTICLES ================= */
+.articles-section {
+ padding: 80px 0;
+ background: #f9f9f7;
+}
+
+.article-card {
+ background: white;
+ border-radius: 20px;
+ overflow: hidden;
+ border: 1px solid #f0f0f0;
+ transition: 0.3s;
+ height: 100%;
+}
+
+.article-card:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 10px 30px rgba(0,0,0,0.08);
+}
+
+.article-img-wrap {
+ position: relative;
+}
+
+.article-img-wrap img {
+ width: 100%;
+ height: 200px;
+ object-fit: cover;
+}
+
+.article-tag {
+ position: absolute;
+ top: 12px;
+ left: 12px;
+ padding: 5px 12px;
+ border-radius: 20px;
+ font-size: 12px;
+ font-weight: 600;
+}
+
+.article-body {
+ padding: 20px;
+}
+
+.article-body h5 {
+ font-weight: 700;
+ color: #1c1c1c;
+ margin-bottom: 8px;
+ line-height: 1.4;
+}
+
+.article-body p {
+ color: #777;
+ font-size: 14px;
+ margin-bottom: 16px;
+}
+
+.article-meta {
+ display: flex;
+ gap: 14px;
+ font-size: 12px;
+ color: #aaa;
+}
+
+.article-card-link {
+ text-decoration: none;
+ color: inherit;
+ display: block;
+}
+
+.article-card-link:hover .article-card {
+ transform: translateY(-5px);
+ box-shadow: 0 10px 30px rgba(0,0,0,0.08);
+}
+
+.article-hero {
+ height: 500px;
+ background-size: cover;
+ background-position: center;
+ position: relative;
+}
+
+.article-hero-overlay {
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(to bottom, rgba(0,0,0,0.2), rgba(0,0,0,0.75));
+ display: flex;
+ align-items: flex-end;
+ padding-bottom: 60px;
+ color: white;
+}
+
+.article-hero-title {
+ font-size: 42px;
+ font-weight: 700;
+ color: white;
+ max-width: 800px;
+ line-height: 1.3;
+}
+
+.article-hero-meta {
+ display: flex;
+ gap: 20px;
+ font-size: 14px;
+ opacity: 0.9;
+ margin-top: 12px;
+}
+
+.article-heading {
+ font-size: 22px;
+ font-weight: 700;
+ color: #2e5d34;
+ margin-bottom: 10px;
+ margin-top: 10px;
+}
+
+.article-text {
+ color: #444;
+ font-size: 16px;
+ line-height: 1.9;
+}
+
+.article-section {
+ border-bottom: 1px solid #f0f0f0;
+ padding-bottom: 28px;
+ margin-bottom: 28px;
+}
+
+.article-section:last-child {
+ border-bottom: none;
+}
+
+/* ================= FIND MY PLANT ================= */
+.find-hero {
+ background: linear-gradient(rgba(0,0,0,0.45), rgba(0,0,0,0.45)),
+ url('https://res.cloudinary.com/dd2nfo3ua/image/upload/v1776895422/quiz_k7ieac.webp') center/cover;
+ padding: 80px 0;
+ color: white;
+}
+
+.find-hero-title {
+ font-size: 52px;
+ font-weight: 700;
+ color: white;
+}
+
+.find-hero-desc {
+ font-size: 18px;
+ opacity: 0.9;
+ max-width: 500px;
+}
+
+.how-section {
+ padding: 60px 0;
+ background: #f9f9f7;
+}
+
+.how-card {
+ padding: 20px;
+}
+
+.how-icon {
+ width: 60px;
+ height: 60px;
+ border-radius: 16px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 22px;
+ margin: 0 auto 14px;
+}
+
+.how-card h6 {
+ font-weight: 700;
+ margin-bottom: 6px;
+}
+
+.how-card p {
+ color: #777;
+ font-size: 14px;
+}
+
+.quiz-section {
+ padding: 80px 0;
+}
+
+.quiz-q {
+ font-weight: 700;
+ margin-bottom: 20px;
+ color: #1c1c1c;
+}
+
+.quiz-options {
+ display: flex;
+ gap: 16px;
+ flex-wrap: wrap;
+}
+
+.quiz-option {
+ flex: 1;
+ min-width: 140px;
+ cursor: pointer;
+}
+
+.quiz-option input {
+ display: none;
+}
+
+.quiz-option-box {
+ border: 2px solid #e0e0e0;
+ border-radius: 16px;
+ padding: 20px;
+ text-align: center;
+ transition: 0.2s;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 8px;
+}
+
+.quiz-option-box i {
+ font-size: 24px;
+}
+
+.quiz-option-box span {
+ font-weight: 600;
+ font-size: 15px;
+}
+
+.quiz-option-box small {
+ color: #888;
+ font-size: 12px;
+}
+
+.quiz-option input:checked + .quiz-option-box {
+ border-color: #2e5d34;
+ background: #f0f9f0;
+}
+
+.quiz-option-box:hover {
+ border-color: #2e5d34;
+}
+
+.no-results-icon {
+ font-size: 60px;
+ color: #2e5d34;
+}
+
+/* ================= FOOTER ================= */
+.footer-pro {
+ background: #f3efe8;
+ padding: 50px 0;
+ margin-top: 80px;
+}
+
+.footer-logo {
+ color: #2e5d34;
+ font-weight: bold;
+}
+
+.footer-title {
+ color: #2e5d34;
+ font-weight: bold;
+ margin-bottom: 15px;
+}
+
+.footer-text {
+ color: #6c757d;
+ font-size: 14px;
+}
+
+.footer-links {
+ list-style: none;
+ padding: 0;
+}
+
+.footer-links li {
+ margin-bottom: 8px;
+}
+
+.footer-links a {
+ text-decoration: none;
+ color: #6c757d;
+}
+
+.footer-links a:hover {
+ color: #198754;
+}
+
+.social-icons a {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 40px;
+ height: 40px;
+ background: #e6e2da;
+ border-radius: 50%;
+ margin-right: 10px;
+ color: #2e5d34;
+ text-decoration: none;
+ font-size: 16px;
+ transition: 0.3s;
+}
+
+.social-icons a:hover {
+ background: #2e5d34;
+ color: white;
+}
+
+.subscribe-box {
+ display: flex;
+ margin-top: 10px;
+}
+
+.subscribe-box input {
+ flex: 1;
+ padding: 8px;
+ border-radius: 8px;
+ border: 1px solid #ddd;
+}
+
+.subscribe-box button {
+ margin-left: 8px;
+ background: #2e5d34;
+ color: white;
+ border: none;
+ padding: 8px 15px;
+ border-radius: 8px;
+}
+
+.footer-bottom {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: 20px;
+ font-size: 14px;
+ color: #6c757d;
+}
+
+.footer-bottom a {
+ margin-left: 15px;
+ text-decoration: none;
+ color: #6c757d;
+}
+
+/* ================= AUTH ================= */
+.auth-box {
+ background: #fdf8f3;
+ border: 1px solid #e8ddd0;
+ border-radius: 24px;
+ padding: 40px;
+}
+
+.auth-icon {
+ width: 60px;
+ height: 60px;
+ background: #e6f4ea;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin: 0 auto;
+ font-size: 24px;
+ color: #2e5d34;
+}
+
+.auth-title {
+ font-size: 26px;
+ font-weight: 700;
+ color: #1c1c1c;
+}
+
+.auth-subtitle {
+ color: #888;
+ font-size: 14px;
+}
+
+.auth-label {
+ font-weight: 600;
+ color: #2e5d34;
+ margin-bottom: 6px;
+ display: block;
+}
+
+.auth-link {
+ color: #2e5d34;
+ font-weight: 600;
+}
+
+/* DARK MODE */
+.dark-mode .auth-box {
+ background: #162816;
+ border-color: #1e3d1e;
+}
+
+.dark-mode .auth-title {
+ color: #ffffff;
+}
+
+.dark-mode .auth-subtitle {
+ color: #8ab08a;
+}
+
+/* ================= RESPONSIVE ================= */
+@media (max-width: 768px) {
+ .plant-form,
+ .contact-form-box {
+ padding: 20px;
+ }
+
+ .hero-title {
+ font-size: 40px;
+ }
+
+ .find-hero-title,
+ .care-hero-title {
+ font-size: 36px;
+ }
+}
\ No newline at end of file
diff --git a/Planteer/templates/Planteer/base.html b/Planteer/templates/Planteer/base.html
new file mode 100644
index 0000000..b5d5eea
--- /dev/null
+++ b/Planteer/templates/Planteer/base.html
@@ -0,0 +1,179 @@
+{% load static %}
+
+
+
+ Planteer
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% block content %}
+ {% endblock %}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Add a Comment
+ + {% if request.user.is_authenticated %} + + + {% else %} ++ You need to be logged in to add a comment +
+ Login + Register +