diff --git a/app/(default)/pricing/page.tsx b/app/(default)/pricing/page.tsx new file mode 100644 index 0000000..18d004b --- /dev/null +++ b/app/(default)/pricing/page.tsx @@ -0,0 +1,137 @@ +"use client"; + +import { useState } from "react"; +import Footer from "@/components/ui/footer"; +import styles from "./pricing.module.css"; + +const Pricing: React.FC = () => { + const checkpoints = [100000, 200000, 300000, 1000000, 5000000]; + const [sliderValue, setSliderValue] = useState(0); + const costPerLine = 0.01; + + const handleSliderChange = (e: React.ChangeEvent) => { + setSliderValue(parseFloat(e.target.value)); + }; + + const formatNumber = (value: number): string => { + if (value >= 1000000) return `${(value / 1000000).toFixed(1)}M`; + if (value >= 1000) return `${(value / 1000).toFixed(1)}k`; + return value.toString(); + }; + + const interpolateValue = (value: number): number => { + if (value <= 0) return checkpoints[0]; + if (value >= checkpoints.length - 1) return checkpoints[checkpoints.length - 1]; + + const lowerIndex = Math.floor(value); + const upperIndex = Math.ceil(value); + const fraction = value - lowerIndex; + + return sendUpdatedCheckpointValue( + checkpoints[lowerIndex] + + fraction * (checkpoints[upperIndex] - checkpoints[lowerIndex]) + ); + }; + + const sendUpdatedCheckpointValue = (value: number):number => { + var updatedCheckPointValue:number=100000 + if(value >100000 && value <= 200000){ + updatedCheckPointValue = 200000; + } else if(value >200000 && value <= 300000){ + updatedCheckPointValue = 300000; + } else if(value >300000 && value <= 1000000){ + updatedCheckPointValue = 1000000; + } else{ + updatedCheckPointValue = 5000000; + } + return updatedCheckPointValue; + } + + const currentCheckpoint = interpolateValue(sliderValue); + const totalPrice = currentCheckpoint * costPerLine; + + return ( +
+
+
+

Transparent Pricing for Every Team Size

+

+ Easily calculate your costs based on your lines of code and get a tailored plan for + your needs. +

+
+
+
+
+
+

Pricing Calculator

+
+
+ {checkpoints.map((point, index) => { + let position; + if (index === 0) position = 0; + else if (index === 1) position = 20; + else if (index === 2) position = 40; + else if (index === 3) position = 75; + else position = 100; + + return ( + + {formatNumber(point)} + + ); + })} +
+ +
+
+ Lines of Code +
+
+ +
+
+

Final Price

+

+ ${totalPrice.toLocaleString()} +

+

+ Based on {formatNumber(currentCheckpoint)} lines of code +

+
+
+
+ +
+
+

+ If your lines of code exceed{" "} + 5 million, we’d love to help with a custom + pricing plan! +

+ + Let’s Talk + +
+
+
+
+
+ ); +}; + +export default Pricing; \ No newline at end of file diff --git a/app/(default)/pricing/pricing.module.css b/app/(default)/pricing/pricing.module.css new file mode 100644 index 0000000..336a05f --- /dev/null +++ b/app/(default)/pricing/pricing.module.css @@ -0,0 +1,112 @@ +.header { + background: linear-gradient(90deg, #f97316, #fb923c); + color: white; + text-align: center; + padding: 4rem 1rem; + border-radius: 0 0 1rem 1rem; + margin-top: 3rem; +} + +.header h1 { + font-size: 3rem; + font-weight: 800; +} + +.header p { + font-size: 1.25rem; + margin-top: 1rem; + opacity: 0.9; +} + +.card { + background-color: #ffffff; + border-radius: 1rem; + box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1); + padding: 4rem; + width: 100%; + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.card:hover { + transform: translateY(-5px); + box-shadow: 0px 8px 20px rgba(0, 0, 0, 0.15); +} + +.card-title { + font-size: 1.75rem; + font-weight: 700; + color: #374151; + text-align: center; + margin-bottom: 2rem; +} + +.card-text { + font-size: 1rem; + color: #6b7280; + text-align: center; + margin-bottom: 5vh; +} + +/* Slider Styling */ +.slider { + -webkit-appearance: none; + appearance: none; + width: 100%; + height: 8px; + background: #e5e7eb; + border-radius: 4px; + outline: none; + transition: background 0.3s; +} + +.slider:hover { + background: #d1d5db; +} + +.slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 24px; + height: 24px; + background-color: #f97316; + border: 3px solid white; + border-radius: 50%; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + cursor: pointer; + transition: transform 0.2s ease; +} + +.slider::-webkit-slider-thumb:hover { + transform: scale(1.2); +} + +.slider::-moz-range-thumb { + width: 24px; + height: 24px; + background-color: #f97316; + border: 3px solid white; + border-radius: 50%; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + cursor: pointer; + transition: transform 0.2s ease; +} + +.slider::-moz-range-thumb:hover { + transform: scale(1.2); +} + +.button { + background: linear-gradient(90deg, #f97316, #fb923c); + color: white; + font-size: 1rem; + font-weight: 600; + padding: 0.75rem 1.5rem; + border-radius: 0.5rem; + text-align: center; + transition: background 0.3s ease, box-shadow 0.3s ease; +} + +.button:hover { + background: linear-gradient(90deg, #fb923c, #f97316); + box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.15); +} \ No newline at end of file diff --git a/components/nav/resourcesSection.tsx b/components/nav/resourcesSection.tsx index 33d79f8..6bd1926 100644 --- a/components/nav/resourcesSection.tsx +++ b/components/nav/resourcesSection.tsx @@ -50,7 +50,7 @@ const resourcesData = [ { icon: , name: "Pricing", - url: "/coming-soon", + url: "/pricing", isInternal: true, }, ],