# Project export: Grotime

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: CruzHacks 2024
- Tagline: A website that uses a predictive model to rate the plantability of different plants given weather conditions at a given time.
- Devpost: https://devpost.com/software/grotime
- GitHub: https://github.com/Parupupapi/itsgrotime-
- Demo: https://itsgrotime-fq6l0wuxi-parupupapis-projects.vercel.app/#
- Team: 2 GitHub contributor(s) — JaCARYK (39 commits), Parupupapi (12 commits)

## Devpost submission (written by the team)

### Inspiration

The UC Santa Cruz Arboretum & Botanic Garden served as the inspiration for Grotime.

### What it does

Grotime, the proposed website, leverages predictive modeling to recommend the most suitable plants based on the prevailing weather conditions at the time of user access.

### How we built it

Front end: Developed using Figma, HTML, and CSS. Back end: Implemented with Python, integrating a Weather API and utilizing Flask.

### Challenges we ran into

Aggregating Weather Data: Faced difficulties in gathering weather data for extended periods to enhance predictive accuracy. Plant Data Acquisition: Encountered challenges in finding comprehensive data on various plants to train and optimize the predictive model.

### Accomplishments we're proud of

Single Score Conversion: Successfully transformed the model's predictions into a cohesive single score for user convenience. Simplistic Design: Accomplished the implementation of a simplistic and user-friendly design for an enhanced user experience.

### What we learned

Front-End and Back-End Integration: Discovered the complexities of seamlessly integrating front-end and back-end functionalities. Dataset Organization: Recognized the pivotal role of well-organized datasets in the success of predictive modeling.

### What's next

Integration with Soil Data: Planning to integrate soil data for more accurate predictions and personalized plant recommendations. Expanding User Base: Aiming to extend the platform's utility beyond the UC Santa Cruz Arboretum, supporting gardeners in diverse locations.

## README (from the GitHub repository)

<h1 align="center" id="title">Grotime</h1>

<p align="center">

<p id="description">It is a ML based planting schedule named "grotime"</p>

[https://weather-application-sooty.vercel.app/](https://gro-time.vercel.app/#/current-location)

  
<h2>🧐 Features</h2>

Here're some of the project's best features:

*   ML trained model to determine at what time cetain plants will grow the best 
*   Real-time Weather
*   5 days forecast
*   Wind speed
*   Userfriendly
*   real time user location

  Whats next for grotime?:
implement a more interactive and clean UI.
see the future implementation prototype made in figma here:
https://www.figma.com/proto/1PSMmaW6ttrnOhq3ECjbVp/CruzHacks-Grotime-HIFI?node-id=1-2&scaling=min-zoom&page-id=0%3A1&starting-point-node-id=1%3A2&t=MBf4qzq4emWoqpTT-1&mode=design

  
<h2>💻 Built with</h2>

Technologies used in the project:

*   HTML5
*   CSS3
*   python
*   Javascript
*   Openweather-Api


## Detected evidence (automated analysis)

Indexed codebase: 10 recognized source files, 52 KB.
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code

## Codebase structure (from repository index)

### Files (16 of 16)

```
.DS_Store
.hintrc
assets/.DS_Store
assets/css/styles.css
assets/images/_Thumbs.db
assets/images/weather_icons/_Thumbs.db
assets/js/api.js
assets/js/app.js
assets/js/module.js
assets/js/route.js
index.html
linear regression/app.py
linear regression/linear regression project/weather_data.csv
linear regression/templates/compare_ratings.html
linear regression/templates/index.html
README.md
```

### Dependencies

No dependency index available.

### Recent commits (newest first)

- Add files via upload
- Update styles.css
- Update styles.css
- Update index.html
- Create app.py
- Delete linear regression/MachineLearningModelForPlantRating.ipynb
- Delete linear regression/linear_regression.py
- Create weather_data.csv
- Delete linear regression/weather_data.csv
- Create compare_ratings.html
- Create index.html
- Update linear_regression.py
- Update README.md
- Update index.html
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update favicon.svg
- Update favicon.svg

## Key source files (fetched from GitHub, selected and truncated for size)

### linear regression/app.py

```python
from flask import Flask, render_template, request
import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression

app = Flask(__name__)

# Load the weather data
data = pd.read_csv('/Users/anisiva/Documents/Github/LastTry/linear regression project/weather_data.csv')

# Prepare the data for training
x = data.drop(['Rating', 'Date'], axis=1).values
y = data['Rating'].values

# Train the Linear Regression model
ml = LinearRegression()
ml.fit(x, y)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/calculate_rating', methods=['POST'])
def calculate_rating():
    plantname = request.form['plantname']
    idealtemp = request.form['idealtemp']
    idealhumidity = request.form['idealhumidity']
    idealsunlight = request.form['idealsunlight']

    rating_output = ml.predict([[int(idealtemp), int(idealtemp), int(idealhumidity), int(idealsunlight)]])
    rating_output = round(rating_output[0], 2)

    return render_template('result.html', plantname=plantname, output=rating_output)

@app.route('/forecast_today', methods=['POST'])
def forecast_today():
    todaytemphigh = request.form['todaytemphigh']
    todaytemplow = request.form['todaytemplow']
    todayhumidity = request.form['todayhumidity']
    todaysunlight = request.form['todaysunlight']

    todayoutput = ml.predict([[int(todaytemphigh), int(todaytemplow), int(todayhumidity), int(todaysunlight)]])
    todayoutput = round(todayoutput[0], 2)

    rating_output = float(request.form['output'])
    difference = abs(rating_output - todayoutput)

    if difference < 10:
        prompt = "You can plant the plant today. Conditions are suitable."
    elif 10 <= difference < 20:
        prompt = "Consider planting the plant with caution. Conditions are moderately suitable."
    else:
        prompt = "It's not recommended to plant the plant today. Conditions are not suitable."

    return render_template('forecast_result.html', todaysoutput=todayoutput, difference=difference, prompt=prompt)

@app.route('/compare_ratings', methods=['POST'])
def compare_ratings():
    plantname = request.form['plantname']
    todaytemphigh = request.form['todaytemphigh']
    todaytemplow = request.form['todaytemplow']
    todayhumidity = request.form['todayhumidity']
    todaysunlight = request.form['todaysunlight']

    rating_output = ml.predict([[int(todaytemphigh), int(todaytemplow), int(todayhumidity), int(todaysunlight)]])
    rating_output = round(rating_output[0], 2)

    output = float(request.form.get('output', 0))

    difference = abs(output - rating_output)

    if difference < 10:
        prompt = "You can plant the plant today. Conditions are suitable."
    elif 10 <= difference < 20:
        prompt = "Consider planting the plant with caution. Conditions are moderately suitable."
    else:
        prompt = "It's not recommended to plant the plant today. Conditions are not suitable."

    return render_template('compare_ratings.html', plantname=plantname, prompt=prompt)

if __name__ == '__main__':
    app.run(debug=True)

```

### assets/js/app.js

```javascript
/**
 * @copyright Grotime cruzhacks 2024 All rights reserved
 */

"use strict";

import { fetchData, url } from "./api.js";
import * as module from "./module.js";

/**
 * Add Event Listener on Multiple Elements
 * @param {NodeList} elements Elements node array
 * @param {string} eventType Event Type e.g.: "click", "mouseover"
 * @param {Function} callback Callback function
 */
function addEventOnElements(elements, eventType, callback) {
  for (const element of elements) element.addEventListener(eventType, callback);
}

// Toggle Search on Mobile Devices

const searchView = document.querySelector("[data-search-view]");
const searchTogglers = document.querySelectorAll("[data-search-toggler]");

const toggleSearch = () => searchView.classList.toggle("active");
addEventOnElements(searchTogglers, "click", toggleSearch);

// Search Integration

const searchField = document.querySelector("[data-search-field]");
const searchResult = document.querySelector("[data-search-result]");

let searchTimeout = null;
const searchTimeoutDuration = 500;

searchField.addEventListener("input", function () {
  searchTimeout ?? clearTimeout(searchTimeout);

  if (!searchField.value) {
    searchResult.classList.remove("active");
    searchResult.innerHTML = "";
    searchField.classList.remove("searching");
  } else {
    searchField.classList.add("searching");
  }

  if (searchField.value) {
    searchTimeout = setTimeout(() => {
      fetchData(url.geo(searchField.value), function (locations) {
        searchField.classList.remove("searching");
        searchResult.classList.add("active");
        searchResult.innerHTML = `
        <ul class="view-list" data-search-list></ul>
        `;

        const /** {NodeList} */ items = [];

        for (const { name, lat, lon, country, state } of locations) {
          const searchItem = document.createElement("li");
          searchItem.classList.add("view-item");

          searchItem.innerHTML = `
            <span class="m-icon">location_on</span>

            <div>
                <p class="item-title">${name}</p>

                <p class="label-2 item-subtitle">${state || ""}, ${country}</p>
            </div>

            <a href="#/weather?lat=${lat}&lon=${lon}" class="item-link has-state" aria-label="${name} weather" data-search-toggler></a>
          `;

          searchResult
            .querySelector("[data-search-list]")
            .appendChild(searchItem);
          items.push(searchItem.querySelector("[data-search-toggler]"));
        }

        addEventOnElements(items, "click", function () {
          toggleSearch();
          searchResult.classlist.remove("active");
        });
      });
    }, searchTimeoutDuration);
  }
});

const container = document.querySelector("[data-container]");
const loading = document.querySelector("[data-loading]");
const currentLocationBtn = document.querySelector(
  "[data-current-location-btn]"
);
const errorContent = document.querySelector("[data-error-content]");

/**
 * Render Weather Data in HTML Page
 * @param {number} lat Latitude
 * @param {number} lon Longitude
 */

export const updateWeather = function (lat, lon) {
  loading.style.display = "grid";
  container.style.overflowY = "hidden";
  container.classList.remove("fade-in");
  errorContent.style.display = "none";

  const currentWeatherSection = document.querySelector(
    "[data-current-weather]"
  );
  const highlightSection = document.querySelector("[data-highlights]");
  const hourlySection = document.querySelector("[data-hourly-forecast]");
  const forecastSection = document.querySelector("[data-5-day-forecast]");

  currentWeatherSection.innerHTML = "";
  highlightSection.innerHTML = "";
  hourlySection.innerHTML = "";
  forecastSection.innerHTML = "";

  if (window.location.hash === "#/current-location") {
    currentLocationBtn.setAttribute("disabled", "");
  } else {
    currentLocationBtn.removeAttribute("disabled");
  }

  // Current Weather Section

  fetchData(url.currentWeather(lat, lon), function (currentWeather) {
    const {
      weather,
      dt: dateUnix,
      sys: { sunrise: sunriseUnixUTC, sunset: sunsetUnixUTC },
      main: { temp, feels_like, pressure, humidity },
      visibility,
      timezone,
    } = currentWeather;
    const [{ description, icon }] = weather;

    const card = document.createElement("div");
    card.classList.add("card", "card-lg", "current-weather-card");

    card.innerHTML = `
      <h2 class="title-2 card-title">Now</h2>

      <div class="wrapper">
          <p class="heading">${parseInt(temp)}°<sup>F</sup></p>

          <img src="./assets/images/weather_icons/${icon}.png" width="64" height="64"
              alt="${description}" class="weather-icon">
      </div>

      <p class="body-3">${description}</p>

      <ul class="meta-list">

          <li class="meta-item">
              <span class="m-icon">calendar_today</span>

              <p class="title-3 meta-text">${module.getDate(
                dateUnix,
                timezone
              )}</p>
          </li>

          <li class="meta-item">
              <span class="m-icon">location_on</span>

              <p class="title-3 meta-text" data-location></p>
          </li>
      </ul>
    `;

    fetchData(url.reverseGeo(lat, lon), function ([{ name, country }]) {
      card.querySelector("[data-location]").innerHTML = `${name}, ${country}`;
    });

    currentWeatherSection.appendChild(card);

    // Today's Highlights

    fetchData(url.airPollution(lat, lon), function (airPollution) {
      const [
        {
          main: { aqi },
          components: { no2, o3, so2, pm2_5 },
        },
      ] = airPollution.list;

      const card = document.createElement("div");
      card.classList.add("card", "card-lg");

      card.innerHTML = `
        <h2 class="title-2" id="highlights-label">Today's Highlights</h2>

          <div class="highlight-list">

              <div class="card card-sm highlight-card one">

                  <h3 
[truncated — 7456 more characters]
```

### index.html

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <!-- Primary Meta Tags -->

    <title>grotime</title>
    <meta name="title" content="grotime">
    <meta name="description" content="grotime">

    <!-- Favicon -->

    <link rel="shortcut icon" href="./favicon.svg" type="image/svg+xml">

    <!-- Google Font -->

    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@400;600&display=swap" rel="stylesheet">

    <!-- Custom CSS -->

    <link rel="stylesheet" href="./assets/css/styles.css">

    <!-- Custom JS Link -->

    <script src="./assets/js/app.js" type="module"></script>
    <script src="./assets/js/route.js" type="module"></script>
</head>
<body>

    <!-- Header -->

    <header class="header">
        <div class="container">
            <a href="#" class="logo">
                <img src="./assets/images/logo.png" width="364" height="58" alt="logo">
            </a>
            <div class="search-view" data-search-view>
                <div class="search-wrapper">
                    <input type="search" name="search" placeholder="Search city..." autocomplete="off"
                        class="search-field" data-search-field>
                    <span class="m-icon leading-icon">Search</span>
                    <button class="icon-btn leading-icon has-state" aria-label="close search" data-search-toggler>
                        <span class="m-icon">arrow_back</span>
                    </button>
                </div>
                <div class="search-result" data-search-result></div>
            </div>

            <div class="header-actions">

                <button class="icon-btn has-state" aria-label="open search" data-search-toggler>
                    <span class="m-icon icon">Search</span>
                </button>

                <a href="#/current-location" class="btn-primary has-state" data-current-location-btn>
                    <span class="m-icon">my_location</span>

                    <span class="span">Current Location</span>
                </a>

            </div>

        </div>
    </header>

    <main>
        <article class="container" data-container>

            <div class="content-left">
                <!-- Current Weather-->

                <section class="section current-weather-card" aria-label="current weather" data-current-weather>
                </section>

                <!-- Forecast -->

                <section class="section forecast" aria-labelledby="forecast-label" data-5-day-forecast="forecast">
                </section>

            </div>

            <div class="content-right">

                <!-- Highlights -->

                <section class="section highlights" aria-labelledby="highlights" data-highlights></section>

                <!-- Hourly Forecast -->

                <section class="section hourly-forecast" aria-label="hourly forecast" data-hourly-forecast></section>

                <!-- Footer -->

                <footer class="footer">
                    <p class="body-3">
                        Discover ideal plants based on weather data. Explore our ML model now.
                             </p>

                    <p class="body-3">
                        Click here: <a href="http://127.0.0.1:5000/" title="ML model" target="_blank"
                            rel="noopener">
                            <img src="./assets/images/logo.png" width="300" height="45" loading="lazy"
                                alt="OpenWeather">
                        </a>
                    </p>
                </footer>

            </div>

            <div class="loading" data-loading></div>

        </article>
    </main>

    <!-- 404 Error -->

    <section class="error-content" data-error-content>

        <h2 class="heading">404</h2>

        <p class="body-1">Page not found!</p>

        <a href="#/weather?lat=40.0932222&lon=-74.9627553" class="btn-primary">
            <span class="span">Go Home</span>
        </a>

    </section>

</body>
</html>

```

### assets/js/api.js

```javascript
/**
 * @copyright Grotime cruzhacks 2024 All rights reserved
 */
'use strict';

const api_key ="e4d474a322c0877f50ad1ce9bfa13d83";

/**
 *
 * @param {string} URL API URL
 * @param {Function} callback callback
 */

export const fetchData = function (URL, callback) {
    fetch(`${URL}&appid=${api_key}`)
      .then((res) => res.json())
      .then((data) => callback(data));
  };
  
  export const url = {
    currentWeather(lat, lon) {
      return `https://api.openweathermap.org/data/2.5/weather?${lat}&${lon}&units=imperial`;
    },
    forecast(lat, lon) {
      return `https://api.openweathermap.org/data/2.5/forecast?${lat}&${lon}&units=imperial`;
    },
    airPollution(lat, lon) {
      return `https://api.openweathermap.org/data/2.5/air_pollution?${lat}&${lon}`;
    },
    reverseGeo(lat, lon) {
      return `https://api.openweathermap.org/geo/1.0/reverse?${lat}&${lon}&limit=5`;
    },
  
    /**
     *
     * @param {string} query Search Query E.G.: "London", "Philadelphia"
     * @returns
     */
  
    geo(query) {
      return `https://api.openweathermap.org/geo/1.0/direct?q=${query}&limit=5`;
    },
  };
```

### assets/js/route.js

```javascript
/**
 * @copyright Grotime cruzhacks 2024 All rights reserved
 */
"use strict";

import { updateWeather, error404 } from "./app.js";
const defaultLocation = "#/weather?lat=40.0932222&lon=-74.9627553";

const currentLocation = function () {
  window.navigator.geolocation.getCurrentPosition(
    (res) => {
      const { latitude, longitude } = res.coords;

      updateWeather(`lat=${latitude}`, `lon=${longitude}`);
    },
    (err) => {
      window.location.hash = defaultLocation;
    }
  );
};

/**
 *
 * @param {string} query Searched query
 */
const searchedLocation = (query) => updateWeather(...query.split("&"));
// updateWeather("lat=51.5073219", "lon=-0.1276474")

const routes = new Map([
  ["/current-location", currentLocation],
  ["/weather", searchedLocation],
]);

const checkHash = function () {
  const requestURL = window.location.hash.slice(1);

  const [route, query] = requestURL.includes
    ? requestURL.split("?")
    : [requestURL];

  routes.get(route) ? routes.get(route)(query) : error404();
};

window.addEventListener("hashchange", checkHash);

window.addEventListener("load", function () {
  if (!window.location.hash) {
    window.location.hash = "#/current-location";
  } else {
    checkHash();
  }
});
```

### linear regression/templates/compare_ratings.html

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Comparison Result</title>
    <style>
        body {
            font-family: 'Arial', sans-serif;
            background-color: #bdecb6; /* Light green background */
            margin: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            min-height: 100vh;
        }

        .container {
            max-width: 500px;
            width: 100%;
            padding: 20px;
            background-color: #ffffff; /* White container background */
            border-radius: 8px;
            box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
            text-align: center;
            overflow: hidden; /* Hide overflowing content */
        }

        .result-container {
            margin-bottom: 20px;
            opacity: 0;
            animation: fade-in 1s forwards;
        }

        .result-message {
            font-size: 18px;
            margin-top: 20px;
            color: #2c3e50; /* Dark gray text */
        }

        #plant-name {
            color: #27ae60; /* Emerald green for plant name */
            font-weight: bold;
        }

        .moving-leaves {
            position: relative;
            top: 0;
            animation: leaves-move 5s alternate infinite ease-in-out; /* Moving leaves */
        }

        @keyframes leaves-move {
            to {
                top: -20px;
            }
        }

        a {
            display: inline-block;
            margin-top: 20px;
            text-decoration: none;
            color: #3498db; /* Dodger blue for link */
            font-weight: bold;
            transition: color 0.3s ease-in-out;
        }

        a:hover {
            color: #2980b9; /* Darker blue on hover */
        }

        /* Background movement animation */
        @keyframes bg-move {
            from {
                background-position: 0 0;
            }
            to {
                background-position: 100% 100%;
            }
        }

        @keyframes fade-in {
            to {
                opacity: 1;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <h1 class="moving-leaves">Comparison Result</h1>

        <div class="result-container">
            <div class="result-message">
                 <span id="plant-name">{{ plantname }}</span> {{ prompt }}
            </div>
        </div>

        <a href="{{ url_for('index') }}">Go Back</a>
    </div>
</body>
</html>

```

### assets/js/module.js

```javascript
/**
 * @copyright Grotime cruzhacks 2024 All rights reserved
 */


"use strict";

export const weekDayNames = [
  "Sunday",
  "Monday",
  "Tuesday",
  "Wednesday",
  "Thursday",
  "Friday",
  "Saturday",
];

export const monthNames = [
  "Jan",
  "Feb",
  "Mar",
  "Apr",
  "May",
  "Jun",
  "Jul",
  "Aug",
  "Sep",
  "Oct",
  "Nov",
  "Dec",
];

/**
 *
 * @param {number} dateUnix Unix date in seconds
 * @param {number} timezone Timezone shift from UTC
 * @returns {string} Date string. Format: "Sunday 10, Jan"
 */

export const getDate = function (dateUnix, timezone) {
  const date = new Date((dateUnix + timezone) * 1000);
  const weekDayName = weekDayNames[date.getUTCDay()];
  const monthName = monthNames[date.getUTCMonth()];

  return `${weekDayName} ${date.getUTCDate()}, ${monthName}`;
};

/**
 *
 * @param {number} timeUnix Unix date in seconds
 * @param {number} timezone Timezone shift from UTC
 * @returns {string} Time string, Format: "HH:MM AM/PM"
 */

export const getTime = function (timeUnix, timezone) {
  const date = new Date((timeUnix + timezone) * 1000);
  const hours = date.getUTCHours();
  const minutes = date.getUTCMinutes();
  const period = hours >= 12 ? "PM" : "AM";

  return `${hours % 12 || 12}:${minutes} ${period}`;
};

/**
 *
 * @param {number} timeUnix Unix date in seconds
 * @param {number} timezone Timezone shift from UTC
 * @returns {string} Time string, Format: "HH AM/PM"
 */

export const getHours = function (timeUnix, timezone) {
  const date = new Date((timeUnix + timezone) * 1000);
  const hours = date.getUTCHours();
  const period = hours >= 12 ? "PM" : "AM";

  return `${hours % 12 || 12} ${period}`;
};

/**
 *
 * @param {number} mps Meters per seconds
 * @returns {number} Kilometers per second
 */
export const mps_to_kmh = (mps) => {
  const mph = mps * 3600;
  return mph / 1000;
};

export const aqiText = {
  1: {
    level: "Good",
    message:
      "Air quality is considered satisfactory, and air pollution poses little or no risk",
  },
  2: {
    level: "Fair",
    message:
      "Air quality is acceptable, however, for some pollutants there may be a moderate health concern for a very small number of people who are unusually sensitive to air pollution",
  },
  3: {
    level: "Moderate",
    message:
      "Members of sensitive groups may experience health effects. The general public is not likely to be affected",
  },
  4: {
    level: "Poor",
    message:
      "Everyone may begin to experience health effects; members of sensitive groups may experience more serious health effects",
  },
  5: {
    level: "Very Poor",
    message:
      "Health warnings of emergency conditions. The entire population is more likely to be affected",
  },
};
```

### linear regression/templates/index.html

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Modern Plant Rating Calculator</title>
    <style>
        body {
            font-family: 'Arial', sans-serif;
            background-color: #bdecb6; /* Light green background */
            margin: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            min-height: 100vh;
            animation: bg-move 8s infinite linear; /* Background movement */
        }

        .container {
            max-width: 500px;
            width: 100%;
            padding: 20px;
            background-color: #ffffff; /* White container background */
            border-radius: 8px;
            box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
            text-align: center;
            overflow: hidden; /* Hide overflowing content */
        }

        .form-group {
            margin-bottom: 20px;
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        label {
            font-size: 16px;
            margin-bottom: 5px;
            color: #333;
        }

        input {
            width: 100%;
            padding: 10px;
            margin-top: 5px;
            box-sizing: border-box;
            border: 1px solid #ccc;
            border-radius: 4px;
            font-size: 14px;
        }

        button {
            background-color: #3498db;
            color: #fff;
            padding: 10px 20px;
            border: none;
            border-radius: 4px;
            font-size: 16px;
            cursor: pointer;
            transition: background-color 0.3s ease-in-out;
        }

        button:hover {
            background-color: #217dbb;
        }

        .moving-leaves {
            position: relative;
            top: 0;
            animation: leaves-move 5s alternate infinite ease-in-out; /* Moving leaves */
        }

        @keyframes leaves-move {
            to {
                top: -20px;
            }
        }

        /* Background movement animation */
        @keyframes bg-move {
            from {
                background-position: 0 0;
            }
            to {
                background-position: 100% 100%;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <h1 class="moving-leaves">Modern Plant Rating Calculator</h1>

        <form id="plantForm" action="{{ url_for('compare_ratings') }}" method="post">
            <div class="form-group">
                <label for="plantname">Plant Name:</label>
                <input type="text" id="plantname" name="plantname" required>
            </div>

            <div class="form-group">
                <label for="idealtemp">Ideal Temperature:</label>
                <input type="text" id="idealtemp" name="idealtemp" required>
            </div>

            <div class="form-group">
                <label for="idealhumidity">Ideal Humidity:</label>
                <input type="text" id="idealhumidity" name="idealhumidity" required>
            </div>

            <div class="form-group">
                <label for="idealsunlight">Ideal Sunlight:</label>
                <input type="text" id="idealsunlight" name="idealsunlight" required>
            </div>

            <div class="form-group">
                <label for="todaytemphigh">Today's Forecasted High Temperature:</label>
                <input type="text" id="todaytemphigh" name="todaytemphigh" required>
            </div>

            <div class="form-group">
                <label for="todaytemplow">Today's Forecasted Low Temperature:</label>
                <input type="text" id="todaytemplow" name="todaytemplow" required>
            </div>

            <div class="form-group">
                <label for="todayhumidity">Today's Forecasted Average Humidity:</label>
                <input type="text" id="todayhumidity" name="todayhumidity" required>
            </div>

            <div class="form-group">
                <label for="todaysunlight">Today's Forecasted Sunlight:</label>
                <input type="text" id="todaysunlight" name="todaysunlight" required>
            </div>

            <button type="button" onclick="calculateRating()">Calculate Rating</button>
        </form>

        <script>
            function calculateRating() {
                // Example: You can add logic here to validate form inputs before submitting
                document.getElementById("plantForm").submit();
            }
        </script>
    </div>
</body>
</html>

```

### assets/css/styles.css

```css
/*-----------------------------------*\
  #style.css
\*-----------------------------------*/

/**
 * copyright 2024 Grotime Cruzhacks. All Rights Reserved.

 */

/*-----------------------------------*\
  #CUSTOM PROPERTY
\*-----------------------------------*/

:root {
  /* Colors */

  --primary: #b5a1e5;
  --on-primary: rgb(58, 150, 99);
  --background: rgb(189, 236, 182);
  --on-background:rgb(189, 236, 182);
  --surface: #1d1c1f;
  --on-surface: rgb(201, 201, 34);
  --on-surface-variant: #46bc28;
  --on-surface-variant-2: #b9b6bf;
  --outline: #3e3d40;
  --bg-aqi-1: #89e589;
  --on-bg-aqi-1: #1f331f;
  --bg-aqi-2: #e5dd89;
  --on-bg-aqi-2: #33311f;
  --bg-aqi-3: #e5c089;
  --on-bg-aqi-3: #332b1f;
  --bg-aqi-4: #e58989;
  --on-bg-aqi-4: #331f1f;
  --bg-aqi-5: #e589b7;
  --on-bg-aqi-5: #331f29;
  --white: hsl(0, 0%, 100%);
  --white-alpha-4: hsla(0, 0%, 100%, 0.04);
  --white-alpha-8: hsla(0, 0%, 100%, 0.08);
  --black-alpha-10: hsla(0, 0%, 0%, 0.1);

  /* Gradient Color */

  --gradient-1: linear-gradient(
    180deg,
    hsla(270, 5%, 7%, 0) 0%,
    hsla(270, 5%, 7%, 0.8) 65%,
    hsl(270, 5%, 7%) 100%
  );
  --gradient-2: linear-gradient(
    180deg,
    hsla(260, 5%, 12%, 0) 0%,
    hsla(260, 5%, 12%, 0.8) 65%,
    hsl(260, 5%, 12%) 100%
  );

  /* Typography */

  /* Font Family */

  --ff-nunito-sans: "Nunito Sans", sans-serif;

  /* Font Size */

  --heading: 5.6rem;
  --title-1: 2rem;
  --title-2: 1.8rem;
  --title-3: 1.6rem;
  --body-1: 2.2rem;
  --body-2: 2rem;
  --body-3: 1.6rem;
  --label-1: 1.4rem;
  --label-2: 1.2rem;

  /* Font Weight */

  --weight-regular: 400;
  --weight-semiBold: 600;

  /* Shadow */

  --shadow-1: 0px 1px 3px hsla(0, 0%, 0%, 0.5);
  --shadow-2: 0px 3px 6px hsla(0, 0%, 0%, 0.4);

  /* Border Radius */

  --radius-28: 28px;
  --radius-16: 16px;
  --radius-pill: 500px;
  --radius-circle: 50%;

  /* Transition */

  --transition-short: 100ms ease;
}

/*-----------------------------------*\
  #RESET
\*-----------------------------------*/

*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

li {
  list-style: none;
}

a,
img,
span,
input,
button {
  display: block;
}

a {
  color: inherit;
  text-decoration: none;
}

img {
  height: auto;
}

input,
button {
  background: none;
  border: none;
  color: inherit;
  font: inherit;
}

input {
  width: 100%;
}

button {
  cursor: pointer;
}

sub {
  vertical-align: baseline;
}

sup {
  vertical-align: top;
}

sub,
sup {
  font-size: 0.75em;
}

html {
  font-family: var(--ff-nunito-sans);
  font-size: 10px;
  scroll-behavior: smooth;
}

body {
  background-color: var(--background);
  color: var(--on-background);
  font-size: var(--body-3);
  overflow: hidden;
  height: 300vh;
}

:focus-visible {
  outline: 2px solid var(--white);
  outline-offset: 2px;
}

::selection {
  background-color: var(--white-alpha-8);
}

::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}

::-webkit-scrollbar-thumb {
  background-color: var(--white-alpha-8);
  border-radius: var(--radius-pill);
}

/*-----------------------------------*\
  #MATERIAL ICON
\*-----------------------------------*/

@font-face {
  font-family: "Material Symbols Rounded";
  font-style: normal;
  font-weight: 400;
  src: url(../font/material-symbol-rounded.woff2) format("woff2");
}

.m-icon {
  font-family: "Material Symbols Rounded";
  font-weight: normal;
  font-style: normal;
  font-size: 2.4rem;
  line-height: 1;
  letter-spacing: normal;
  text-transform: normal;
  white-space: nowrap;
  word-wrap: normal;
  direction: ltr;
  font-feature-settings: "liga";
  -webkit-font-feature-settings: "liga";
  -webkit-font-smoothing: antialiased;
  height: 1em;
  width: 1em;
  overflow: hidden;
}

/*-----------------------------------*\
  #REUSED STYLE
\*-----------------------------------*/

.container {
  max-width: 1600px;
  width: 100%;
  margin-inline: auto;
  padding: 16px;
}

.icon-btn {
  background-color: var(--white-alpha-8);
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-circle);
}

.has-state {
  position: relative;
}

.has-state:hover {
  box-shadow: var(--shadow-1);
}

.has-state:is(:focus, :focus-visible) {
  box-shadow: none;
}

.has-state::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  clip-path: circle(100% at 50% 50%);
  transition: var(--transition-short);
}

.has-state:hover::before {
  background-color: var(--white-alpha-4);
}

.has-state:is(:focus, :focus-visible)::before {
  background-color: var(--white-alpha-8);
  animation: ripple 250ms ease forwards;
}

@keyframes ripple {
  0% {
    clip-path: circle(0% at 50% 50%);
  }
  100% {
    clip-path: circle(100% at 50% 50%);
  }
}

.btn-primary {
  background-color: var(--primary);
  color: var(--on-primary);
  height: 48px;
  line-height: 48px;
  max-width: max-content;
  display: flex;
  align-items: center;
  gap: 16px;
  padding-inline: 16px;
  border-radius: var(--radius-pill);
}

.btn-primary .span {
  font-weight: var(--weight-semiBold);
}

.btn-primary[disabled] {
  background-color: var(--outline);
  color: var(--on-surface-variant);
  cursor: not-allowed;
}

.btn-primary[disabled]::before {
  display: none;
}

.card {
  background-color: var(--surface);
  color: var(--on-surface);
}

.card-lg {
  border-radius: var(--radius-28);
  padding: 20px;
}

.card-sm {
  padding: 16px;
  border-radius: var(--radius-16);
}

.heading {
  color: var(--white);
  font-size: var(--heading);
  line-height: 1.1;
}

.title-1 {
  font-size: var(--title-1);
}

.title-2 {
  font-size: var(--title-2);
  margin-block-end: 12px;
}

.title-3 {
  font-size: var(--title-3);
  font-weight: var(--weight-semiBold);
}

.body-1 {
  font-size: var(--body-1);
}

.body-2 {
  font-size: var(--body-2);
  font-weight: var(--weight-semiBold);
}

.body-3 {
  font-size: var(--body-3);
  text-transform: capitalize;
}

.label-1 {
  font-size: var(--label-1);
}

.label-2 {
  font-size: var(--label-2);
}

.fad
[truncated — 12770 more characters]
```