deploy: polish public marketing pages
This commit is contained in:
parent
9dfee33e49
commit
14cb8ce1fc
14 changed files with 714 additions and 153 deletions
|
|
@ -183,7 +183,8 @@
|
||||||
transition:
|
transition:
|
||||||
color 0.2s ease,
|
color 0.2s ease,
|
||||||
text-decoration-color 0.2s ease,
|
text-decoration-color 0.2s ease,
|
||||||
opacity 0.2s ease;
|
opacity 0.2s ease,
|
||||||
|
transform 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover,
|
a:hover,
|
||||||
|
|
@ -202,7 +203,8 @@
|
||||||
transition:
|
transition:
|
||||||
color 0.2s ease,
|
color 0.2s ease,
|
||||||
text-decoration-color 0.2s ease,
|
text-decoration-color 0.2s ease,
|
||||||
opacity 0.2s ease;
|
opacity 0.2s ease,
|
||||||
|
transform 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
a[href]:hover,
|
a[href]:hover,
|
||||||
|
|
@ -211,6 +213,29 @@
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button a,
|
||||||
|
[role="button"] a,
|
||||||
|
.bg-primary a,
|
||||||
|
.text-primary-foreground a {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
button a:hover,
|
||||||
|
button a:focus,
|
||||||
|
[role="button"] a:hover,
|
||||||
|
[role="button"] a:focus,
|
||||||
|
.bg-primary a:hover,
|
||||||
|
.bg-primary a:focus,
|
||||||
|
.text-primary-foreground a:hover,
|
||||||
|
.text-primary-foreground a:focus {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
li {
|
||||||
|
text-wrap: pretty;
|
||||||
|
}
|
||||||
|
|
||||||
a:focus-visible,
|
a:focus-visible,
|
||||||
button:focus-visible,
|
button:focus-visible,
|
||||||
[role="button"]:focus-visible,
|
[role="button"]:focus-visible,
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,10 @@ function LocationCard({
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Link href={href} className="group block h-full">
|
<Link href={href} className="group block h-full">
|
||||||
<PublicSurface className="h-full p-5 transition-all hover:-translate-y-0.5 hover:shadow-[0_26px_65px_rgba(0,0,0,0.12)] md:p-6">
|
<PublicInset className="h-full p-4 transition-all hover:-translate-y-0.5 hover:border-primary/25 hover:shadow-[0_20px_48px_rgba(0,0,0,0.09)] md:p-5">
|
||||||
<div className="flex items-start justify-between gap-4">
|
<div className="flex items-start justify-between gap-4">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<h3 className="text-xl font-semibold text-foreground transition-colors group-hover:text-primary">
|
<h3 className="text-lg font-semibold text-foreground transition-colors group-hover:text-primary">
|
||||||
{city}
|
{city}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-muted-foreground">{zipCode}</p>
|
<p className="text-sm text-muted-foreground">{zipCode}</p>
|
||||||
|
|
@ -49,15 +49,15 @@ function LocationCard({
|
||||||
<ArrowRight className="h-4 w-4" />
|
<ArrowRight className="h-4 w-4" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<PublicInset className="mt-5">
|
<div className="mt-4 rounded-[1.1rem] border border-border/45 bg-background/60 p-3">
|
||||||
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-primary/75">
|
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-primary/75">
|
||||||
Popular Areas
|
Popular Areas
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-2 text-sm leading-relaxed text-muted-foreground">
|
<p className="mt-2 text-sm leading-relaxed text-muted-foreground">
|
||||||
{neighborhoods.slice(0, 2).join(", ")}
|
{neighborhoods.slice(0, 2).join(", ")}
|
||||||
</p>
|
</p>
|
||||||
</PublicInset>
|
</div>
|
||||||
</PublicSurface>
|
</PublicInset>
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -222,7 +222,7 @@ export default function ServiceAreasPage() {
|
||||||
</PublicSurface>
|
</PublicSurface>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="mt-12 space-y-12">
|
<section className="mt-12 space-y-8">
|
||||||
{[
|
{[
|
||||||
{
|
{
|
||||||
title: "Salt Lake County",
|
title: "Salt Lake County",
|
||||||
|
|
@ -243,19 +243,24 @@ export default function ServiceAreasPage() {
|
||||||
items: utahCounty,
|
items: utahCounty,
|
||||||
},
|
},
|
||||||
].map((section) => (
|
].map((section) => (
|
||||||
<div key={section.title} className="space-y-5">
|
<PublicSurface key={section.title} className="p-5 md:p-6">
|
||||||
<div>
|
<div className="flex flex-col gap-4 border-b border-border/55 pb-5 lg:flex-row lg:items-end lg:justify-between">
|
||||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-primary/80">
|
<div>
|
||||||
Coverage Area
|
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-primary/80">
|
||||||
</p>
|
Coverage Area
|
||||||
<h2 className="mt-2 text-3xl font-semibold tracking-tight text-balance">
|
</p>
|
||||||
{section.title}
|
<h2 className="mt-2 text-3xl font-semibold tracking-tight text-balance">
|
||||||
</h2>
|
{section.title}
|
||||||
<p className="mt-2 max-w-3xl text-base leading-relaxed text-muted-foreground">
|
</h2>
|
||||||
{section.description}
|
<p className="mt-2 max-w-3xl text-base leading-relaxed text-muted-foreground">
|
||||||
</p>
|
{section.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="w-fit rounded-full border border-border/55 bg-background/70 px-3 py-1 text-sm text-muted-foreground">
|
||||||
|
{section.items.length} cities
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
<div className="mt-5 grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||||
{section.items.map((location) => (
|
{section.items.map((location) => (
|
||||||
<LocationCard
|
<LocationCard
|
||||||
key={location.slug}
|
key={location.slug}
|
||||||
|
|
@ -266,11 +271,11 @@ export default function ServiceAreasPage() {
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</PublicSurface>
|
||||||
))}
|
))}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="mt-12 grid gap-6 lg:grid-cols-[1.15fr_0.85fr]">
|
<section className="mt-12 grid gap-6 lg:grid-cols-2">
|
||||||
<PublicSurface>
|
<PublicSurface>
|
||||||
<h2 className="text-3xl font-semibold tracking-tight text-balance">
|
<h2 className="text-3xl font-semibold tracking-tight text-balance">
|
||||||
Why businesses choose Rocky Mountain Vending
|
Why businesses choose Rocky Mountain Vending
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
import { notFound } from "next/navigation"
|
import { notFound } from "next/navigation"
|
||||||
import { loadImageMapping } from "@/lib/wordpress-content"
|
|
||||||
import {
|
import {
|
||||||
generateRegistryMetadata,
|
generateRegistryMetadata,
|
||||||
generateRegistryStructuredData,
|
generateRegistryStructuredData,
|
||||||
} from "@/lib/seo"
|
} from "@/lib/seo"
|
||||||
import { getPageBySlug } from "@/lib/wordpress-data-loader"
|
import { getPageBySlug } from "@/lib/wordpress-data-loader"
|
||||||
import { cleanWordPressContent } from "@/lib/clean-wordPress-content"
|
|
||||||
import { FAQSection } from "@/components/faq-section"
|
import { FAQSection } from "@/components/faq-section"
|
||||||
import { ServiceAreasSection } from "@/components/service-areas-section"
|
import { ServiceAreasSection } from "@/components/service-areas-section"
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
|
|
@ -58,21 +56,13 @@ export default async function RepairsPage() {
|
||||||
notFound()
|
notFound()
|
||||||
}
|
}
|
||||||
|
|
||||||
let imageMapping: any = {}
|
|
||||||
try {
|
|
||||||
imageMapping = loadImageMapping()
|
|
||||||
} catch (e) {
|
|
||||||
imageMapping = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract FAQs from content
|
// Extract FAQs from content
|
||||||
const faqs: Array<{ question: string; answer: string }> = []
|
const faqs: Array<{ question: string; answer: string }> = []
|
||||||
let contentWithoutFAQs = page.content || ""
|
|
||||||
let contentWithoutVirtualServices = ""
|
|
||||||
let virtualServicesContent = ""
|
let virtualServicesContent = ""
|
||||||
|
|
||||||
if (page.content) {
|
if (page.content) {
|
||||||
const contentStr = String(page.content)
|
const contentStr = String(page.content)
|
||||||
|
let strippedContent = contentStr
|
||||||
|
|
||||||
// Extract FAQ items from accordion structure
|
// Extract FAQ items from accordion structure
|
||||||
const questionMatches = contentStr.matchAll(
|
const questionMatches = contentStr.matchAll(
|
||||||
|
|
@ -112,40 +102,28 @@ export default async function RepairsPage() {
|
||||||
if (faqs.length > 0) {
|
if (faqs.length > 0) {
|
||||||
const faqSectionRegex =
|
const faqSectionRegex =
|
||||||
/<h2[^>]*>.*?Answers\s+To\s+Common\s+Questions.*?<\/h2>[\s\S]*?(?=<h2[^>]*>.*?Virtual\s+Services|<h2[^>]*>.*?Service\s+Area|$)/i
|
/<h2[^>]*>.*?Answers\s+To\s+Common\s+Questions.*?<\/h2>[\s\S]*?(?=<h2[^>]*>.*?Virtual\s+Services|<h2[^>]*>.*?Service\s+Area|$)/i
|
||||||
contentWithoutFAQs = contentStr.replace(faqSectionRegex, "").trim()
|
strippedContent = contentStr.replace(faqSectionRegex, "").trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract Virtual Services section
|
// Extract Virtual Services section
|
||||||
const virtualServicesRegex =
|
const virtualServicesRegex =
|
||||||
/<h2[^>]*>.*?Virtual\s+Services.*?<\/h2>([\s\S]*?)(?=<h2[^>]*>.*?Service\s+Area|$)/i
|
/<h2[^>]*>.*?Virtual\s+Services.*?<\/h2>([\s\S]*?)(?=<h2[^>]*>.*?Service\s+Area|$)/i
|
||||||
const virtualMatch = contentStr.match(virtualServicesRegex)
|
const virtualMatch = strippedContent.match(virtualServicesRegex)
|
||||||
if (virtualMatch) {
|
if (virtualMatch) {
|
||||||
virtualServicesContent = virtualMatch[1]
|
virtualServicesContent = virtualMatch[1]
|
||||||
// Remove Virtual Services from main content
|
|
||||||
contentWithoutVirtualServices = contentWithoutFAQs
|
|
||||||
.replace(virtualServicesRegex, "")
|
|
||||||
.trim()
|
|
||||||
} else {
|
|
||||||
contentWithoutVirtualServices = contentWithoutFAQs
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = contentWithoutVirtualServices ? (
|
|
||||||
<div className="max-w-none">
|
|
||||||
{cleanWordPressContent(String(contentWithoutVirtualServices), {
|
|
||||||
imageMapping,
|
|
||||||
pageTitle: page.title,
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<p className="text-muted-foreground">No content available.</p>
|
|
||||||
)
|
|
||||||
|
|
||||||
const structuredData = generateRegistryStructuredData("repairs", {
|
const structuredData = generateRegistryStructuredData("repairs", {
|
||||||
datePublished: page.date,
|
datePublished: page.date,
|
||||||
dateModified: page.modified || page.date,
|
dateModified: page.modified || page.date,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const excerpt = String(page.excerpt || "")
|
||||||
|
.replace(/<[^>]+>/g, " ")
|
||||||
|
.replace(/\s+/g, " ")
|
||||||
|
.trim()
|
||||||
|
|
||||||
const surfaceCardClass =
|
const surfaceCardClass =
|
||||||
"rounded-[var(--public-surface-radius)] border border-border/70 bg-[linear-gradient(180deg,rgba(255,255,255,0.98),rgba(255,251,243,0.96))] shadow-[var(--public-surface-shadow)]"
|
"rounded-[var(--public-surface-radius)] border border-border/70 bg-[linear-gradient(180deg,rgba(255,255,255,0.98),rgba(255,251,243,0.96))] shadow-[var(--public-surface-shadow)]"
|
||||||
const insetCardClass =
|
const insetCardClass =
|
||||||
|
|
@ -171,37 +149,14 @@ export default async function RepairsPage() {
|
||||||
align="center"
|
align="center"
|
||||||
className="mb-8"
|
className="mb-8"
|
||||||
eyebrow="Repair Services"
|
eyebrow="Repair Services"
|
||||||
title={page.title || "Vending Machine Repairs and Service"}
|
title="Vending machine repairs and service for Utah businesses"
|
||||||
description={
|
description={
|
||||||
"Rocky Mountain Vending delivers expert vending machine repair and maintenance services to keep your business thriving."
|
"Get help with payment issues, refrigeration problems, machine errors, and ongoing maintenance from a local vending service team."
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<p className="mx-auto max-w-3xl text-base leading-relaxed text-muted-foreground md:text-lg">
|
<p className="mx-auto max-w-3xl text-base leading-relaxed text-muted-foreground md:text-lg">
|
||||||
Rocky Mountain Vending delivers expert{" "}
|
{excerpt ||
|
||||||
<Link
|
"Rocky Mountain Vending helps businesses across Davis, Salt Lake, and Utah counties keep machines running with practical repair, maintenance, and support guidance."}
|
||||||
href="/services/repairs"
|
|
||||||
className="text-primary hover:underline font-semibold"
|
|
||||||
>
|
|
||||||
vending machine repair
|
|
||||||
</Link>{" "}
|
|
||||||
and maintenance services to keep your business thriving. From
|
|
||||||
resolving jammed coin slots and refrigeration issues to fixing
|
|
||||||
non-dispensing machines, our skilled technicians ensure reliable
|
|
||||||
performance. For all your{" "}
|
|
||||||
<Link
|
|
||||||
href="/services/parts"
|
|
||||||
className="text-primary hover:underline"
|
|
||||||
>
|
|
||||||
vending machine parts
|
|
||||||
</Link>{" "}
|
|
||||||
needs and professional{" "}
|
|
||||||
<Link
|
|
||||||
href="/services/moving"
|
|
||||||
className="text-primary hover:underline"
|
|
||||||
>
|
|
||||||
vending machine moving
|
|
||||||
</Link>{" "}
|
|
||||||
services, contact us today for fast, professional solutions!
|
|
||||||
</p>
|
</p>
|
||||||
</PublicPageHeader>
|
</PublicPageHeader>
|
||||||
{/* Images Carousel */}
|
{/* Images Carousel */}
|
||||||
|
|
@ -211,15 +166,71 @@ export default async function RepairsPage() {
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{contentWithoutVirtualServices ? (
|
<section className="py-16 md:py-20 bg-background">
|
||||||
<section className="py-16 md:py-20 bg-background">
|
<div className="container mx-auto grid max-w-6xl gap-6 px-4 lg:grid-cols-[1.08fr_0.92fr]">
|
||||||
<div className="container mx-auto px-4 max-w-4xl">
|
<PublicSurface className="h-full">
|
||||||
<PublicSurface>
|
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-primary/80">
|
||||||
<div className="max-w-none">{content}</div>
|
Repair Overview
|
||||||
</PublicSurface>
|
</p>
|
||||||
</div>
|
<h2 className="mt-3 text-3xl font-semibold tracking-tight text-balance">
|
||||||
</section>
|
Clear next steps when a machine is down, rejecting payments, or
|
||||||
) : null}
|
needs service.
|
||||||
|
</h2>
|
||||||
|
<p className="mt-4 text-base leading-7 text-muted-foreground md:text-lg md:leading-8">
|
||||||
|
We help with common vending issues like bill acceptor problems,
|
||||||
|
refrigeration failures, card reader troubleshooting, controller
|
||||||
|
errors, and recurring maintenance needs. If the issue can be
|
||||||
|
handled virtually, we can talk through that too.
|
||||||
|
</p>
|
||||||
|
<div className="mt-6 flex flex-col gap-3 sm:flex-row">
|
||||||
|
<Link
|
||||||
|
href="#request-service"
|
||||||
|
className="inline-flex min-h-11 items-center justify-center rounded-full bg-primary px-5 text-sm font-medium text-primary-foreground transition hover:opacity-95"
|
||||||
|
>
|
||||||
|
Request Service
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/services/parts"
|
||||||
|
className="inline-flex min-h-11 items-center justify-center rounded-full border border-border bg-background px-5 text-sm font-medium text-foreground transition hover:border-primary/35 hover:text-primary"
|
||||||
|
>
|
||||||
|
Need Parts Instead?
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</PublicSurface>
|
||||||
|
<PublicInset className="h-full p-5 md:p-6">
|
||||||
|
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-primary/80">
|
||||||
|
Before You Reach Out
|
||||||
|
</p>
|
||||||
|
<h2 className="mt-3 text-2xl font-semibold tracking-tight text-balance">
|
||||||
|
The more detail you share, the faster we can point you in the
|
||||||
|
right direction.
|
||||||
|
</h2>
|
||||||
|
<ul className="mt-5 space-y-3">
|
||||||
|
<li className="flex items-start gap-3">
|
||||||
|
<CheckCircle2 className="mt-0.5 h-5 w-5 shrink-0 text-primary" />
|
||||||
|
<span className="text-sm leading-6 text-muted-foreground">
|
||||||
|
Include the machine model, brand, and whether the issue is
|
||||||
|
intermittent or constant.
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start gap-3">
|
||||||
|
<CheckCircle2 className="mt-0.5 h-5 w-5 shrink-0 text-primary" />
|
||||||
|
<span className="text-sm leading-6 text-muted-foreground">
|
||||||
|
Tell us if the problem is payment-related, refrigeration,
|
||||||
|
dispensing, display errors, or a recent setup change.
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start gap-3">
|
||||||
|
<CheckCircle2 className="mt-0.5 h-5 w-5 shrink-0 text-primary" />
|
||||||
|
<span className="text-sm leading-6 text-muted-foreground">
|
||||||
|
Photos or short videos can make remote triage much easier
|
||||||
|
before an on-site visit is scheduled.
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</PublicInset>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
{/* Services Section */}
|
{/* Services Section */}
|
||||||
<section className="py-20 md:py-28 bg-muted/30">
|
<section className="py-20 md:py-28 bg-muted/30">
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,74 @@
|
||||||
import { notFound } from "next/navigation"
|
import { notFound } from "next/navigation"
|
||||||
import { loadImageMapping } from "@/lib/wordpress-content"
|
import type { Metadata } from "next"
|
||||||
import type { ImageMapping } from "@/lib/wordpress-content"
|
import Image from "next/image"
|
||||||
|
import Link from "next/link"
|
||||||
|
import {
|
||||||
|
CheckCircle2,
|
||||||
|
CreditCard,
|
||||||
|
Refrigerator,
|
||||||
|
ShoppingCart,
|
||||||
|
} from "lucide-react"
|
||||||
import { generateSEOMetadata, generateStructuredData } from "@/lib/seo"
|
import { generateSEOMetadata, generateStructuredData } from "@/lib/seo"
|
||||||
import { getPageBySlug } from "@/lib/wordpress-data-loader"
|
import { getPageBySlug } from "@/lib/wordpress-data-loader"
|
||||||
import { cleanWordPressContent } from "@/lib/clean-wordPress-content"
|
import {
|
||||||
import type { Metadata } from "next"
|
PublicInset,
|
||||||
import { PublicPageHeader, PublicSurface } from "@/components/public-surface"
|
PublicPageHeader,
|
||||||
|
PublicSectionHeader,
|
||||||
|
PublicSurface,
|
||||||
|
} from "@/components/public-surface"
|
||||||
import { GetFreeMachineCta } from "@/components/get-free-machine-cta"
|
import { GetFreeMachineCta } from "@/components/get-free-machine-cta"
|
||||||
import { Breadcrumbs } from "@/components/breadcrumbs"
|
import { Breadcrumbs } from "@/components/breadcrumbs"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
|
||||||
const WORDPRESS_SLUG = "vending-machines-for-sale-in-utah"
|
const WORDPRESS_SLUG = "vending-machines-for-sale-in-utah"
|
||||||
|
|
||||||
|
const machineHighlights = [
|
||||||
|
"Snack, drink, combo, and card-reader-ready equipment options",
|
||||||
|
"New and used machine guidance based on traffic, budget, and product mix",
|
||||||
|
"Local help with payment hardware, installation planning, and next-step questions",
|
||||||
|
]
|
||||||
|
|
||||||
|
const machineOptions = [
|
||||||
|
{
|
||||||
|
title: "Snack and drink machines",
|
||||||
|
description:
|
||||||
|
"Traditional snack, beverage, and combo machines for breakrooms, customer spaces, and mixed-traffic locations.",
|
||||||
|
icon: ShoppingCart,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Cashless payment hardware",
|
||||||
|
description:
|
||||||
|
"Card reader and mobile-payment options that help modernize older machines or support new installs.",
|
||||||
|
icon: CreditCard,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Refrigerated equipment",
|
||||||
|
description:
|
||||||
|
"Cold drink and refrigerated machine options for workplaces that need dependable temperature-controlled service.",
|
||||||
|
icon: Refrigerator,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const buyingSteps = [
|
||||||
|
{
|
||||||
|
title: "Tell us about the location",
|
||||||
|
body: "We learn about traffic, available space, product needs, and whether you are comparing free placement with a direct purchase.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Compare the right machine options",
|
||||||
|
body: "We help narrow down machine styles, payment hardware, and new-versus-used tradeoffs so you are looking at realistic fits.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Plan install and support",
|
||||||
|
body: "Once you know what you want, we can talk through delivery, setup, payment configuration, and any follow-up service needs.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
function normalizeWpImageUrl(url?: string) {
|
||||||
|
if (!url) return null
|
||||||
|
return url.replace("https:///", "https://rockymountainvending.com/")
|
||||||
|
}
|
||||||
|
|
||||||
export async function generateMetadata(): Promise<Metadata> {
|
export async function generateMetadata(): Promise<Metadata> {
|
||||||
const page = getPageBySlug(WORDPRESS_SLUG)
|
const page = getPageBySlug(WORDPRESS_SLUG)
|
||||||
|
|
||||||
|
|
@ -39,13 +97,6 @@ export default async function MachinesForSalePage() {
|
||||||
notFound()
|
notFound()
|
||||||
}
|
}
|
||||||
|
|
||||||
let imageMapping: ImageMapping = {}
|
|
||||||
try {
|
|
||||||
imageMapping = loadImageMapping()
|
|
||||||
} catch {
|
|
||||||
imageMapping = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
const structuredData = (() => {
|
const structuredData = (() => {
|
||||||
try {
|
try {
|
||||||
return generateStructuredData({
|
return generateStructuredData({
|
||||||
|
|
@ -70,6 +121,16 @@ export default async function MachinesForSalePage() {
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
|
const heroImage =
|
||||||
|
normalizeWpImageUrl(page.images?.[0]?.url) ??
|
||||||
|
"https://rockymountainvending.com/wp-content/uploads/2024/01/EH0A1551-HDR.webp"
|
||||||
|
const comboImage =
|
||||||
|
normalizeWpImageUrl(page.images?.[1]?.url) ??
|
||||||
|
"https://rockymountainvending.com/wp-content/uploads/2022/06/Seage-HY900-Combo.webp"
|
||||||
|
const paymentImage =
|
||||||
|
normalizeWpImageUrl(page.images?.[2]?.url) ??
|
||||||
|
"https://rockymountainvending.com/wp-content/uploads/2024/01/Parlevel-Pay-Plus.jpg"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<script
|
<script
|
||||||
|
|
@ -88,19 +149,168 @@ export default async function MachinesForSalePage() {
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<PublicPageHeader
|
<PublicPageHeader
|
||||||
|
align="center"
|
||||||
eyebrow="Machine Sales"
|
eyebrow="Machine Sales"
|
||||||
title={page.title || "Vending Machines for Sale in Utah"}
|
title="Compare vending machines, payment hardware, and purchase options with a local Utah team."
|
||||||
description="Compare machine options, payment hardware, and support with help from the Rocky Mountain Vending team."
|
description="If you are looking at buying equipment instead of free placement, we can help you compare machine styles, payment systems, and next-step support without sending you through a generic catalog dump."
|
||||||
/>
|
>
|
||||||
|
<div className="flex flex-col items-center justify-center gap-3 sm:flex-row">
|
||||||
|
<Button asChild size="lg" className="min-h-11 rounded-full px-6">
|
||||||
|
<Link href="/contact-us#contact-form">Ask About Sales</Link>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
asChild
|
||||||
|
size="lg"
|
||||||
|
variant="outline"
|
||||||
|
className="min-h-11 rounded-full px-6"
|
||||||
|
>
|
||||||
|
<Link href="/products">Browse Product Listings</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PublicPageHeader>
|
||||||
|
|
||||||
<PublicSurface className="mt-10">
|
<section className="mt-10 grid gap-6 xl:grid-cols-[1.05fr_0.95fr]">
|
||||||
<div className="max-w-none">
|
<PublicSurface className="flex h-full flex-col justify-center">
|
||||||
{cleanWordPressContent(String(page.content || ""), {
|
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-primary/80">
|
||||||
imageMapping,
|
Sales Overview
|
||||||
pageTitle: page.title,
|
</p>
|
||||||
|
<h2 className="mt-3 text-3xl font-semibold tracking-tight text-balance">
|
||||||
|
Buying a machine should feel clear before you spend money.
|
||||||
|
</h2>
|
||||||
|
<p className="mt-4 text-base leading-7 text-muted-foreground md:text-lg md:leading-8">
|
||||||
|
We help Utah businesses sort through machine type, payment
|
||||||
|
hardware, and install considerations so you can decide whether a
|
||||||
|
direct purchase is the right move for your location.
|
||||||
|
</p>
|
||||||
|
<ul className="mt-6 space-y-3">
|
||||||
|
{machineHighlights.map((highlight) => (
|
||||||
|
<li key={highlight} className="flex items-start gap-3">
|
||||||
|
<CheckCircle2 className="mt-0.5 h-5 w-5 shrink-0 text-primary" />
|
||||||
|
<span className="text-sm leading-6 text-foreground md:text-base">
|
||||||
|
{highlight}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</PublicSurface>
|
||||||
|
<div className="relative min-h-[320px] overflow-hidden rounded-[var(--public-surface-radius)] border border-border/65 bg-[linear-gradient(180deg,rgba(255,255,255,0.98),rgba(255,249,240,0.96))] p-3 shadow-[0_20px_52px_rgba(15,23,42,0.075)]">
|
||||||
|
<Image
|
||||||
|
src={heroImage}
|
||||||
|
alt="Vending machine option available for sale in Utah"
|
||||||
|
fill
|
||||||
|
className="rounded-[calc(var(--public-surface-radius)-0.45rem)] object-cover"
|
||||||
|
sizes="(max-width: 1280px) 100vw, 560px"
|
||||||
|
priority
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mt-12">
|
||||||
|
<PublicSectionHeader
|
||||||
|
eyebrow="Machine Options"
|
||||||
|
title="What businesses usually want help comparing"
|
||||||
|
description="Most sales conversations come down to the machine type, payment setup, and whether a direct purchase makes more sense than placement."
|
||||||
|
className="mx-auto mb-8 max-w-3xl text-center"
|
||||||
|
/>
|
||||||
|
<div className="grid gap-4 lg:grid-cols-3">
|
||||||
|
{machineOptions.map((option) => {
|
||||||
|
const Icon = option.icon
|
||||||
|
return (
|
||||||
|
<PublicInset key={option.title} className="h-full p-5 md:p-6">
|
||||||
|
<div className="flex h-11 w-11 items-center justify-center rounded-full bg-primary/10 text-primary">
|
||||||
|
<Icon className="h-5 w-5" />
|
||||||
|
</div>
|
||||||
|
<h3 className="mt-4 text-xl font-semibold tracking-tight text-foreground">
|
||||||
|
{option.title}
|
||||||
|
</h3>
|
||||||
|
<p className="mt-3 text-sm leading-6 text-muted-foreground md:text-base">
|
||||||
|
{option.description}
|
||||||
|
</p>
|
||||||
|
</PublicInset>
|
||||||
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</PublicSurface>
|
</section>
|
||||||
|
|
||||||
|
<section className="mt-12 grid gap-6 lg:grid-cols-[0.98fr_1.02fr]">
|
||||||
|
<PublicSurface className="overflow-hidden p-0">
|
||||||
|
<div className="grid gap-0 md:grid-cols-2">
|
||||||
|
<div className="relative min-h-[260px]">
|
||||||
|
<Image
|
||||||
|
src={comboImage}
|
||||||
|
alt="Combo vending machine for sale"
|
||||||
|
fill
|
||||||
|
className="object-cover"
|
||||||
|
sizes="(max-width: 768px) 100vw, 360px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col justify-center p-5 md:p-6">
|
||||||
|
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-primary/80">
|
||||||
|
New vs. Used
|
||||||
|
</p>
|
||||||
|
<h2 className="mt-3 text-2xl font-semibold tracking-tight text-balance">
|
||||||
|
We can help you sort through budget, features, and condition.
|
||||||
|
</h2>
|
||||||
|
<p className="mt-3 text-sm leading-6 text-muted-foreground md:text-base">
|
||||||
|
Some buyers need dependable starter equipment. Others need a
|
||||||
|
cleaner, more modern machine with stronger payment support.
|
||||||
|
We can talk through both without pushing you into the wrong
|
||||||
|
setup.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PublicSurface>
|
||||||
|
<PublicSurface className="overflow-hidden p-0">
|
||||||
|
<div className="grid gap-0 md:grid-cols-2">
|
||||||
|
<div className="order-2 flex flex-col justify-center p-5 md:order-1 md:p-6">
|
||||||
|
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-primary/80">
|
||||||
|
Payment Hardware
|
||||||
|
</p>
|
||||||
|
<h2 className="mt-3 text-2xl font-semibold tracking-tight text-balance">
|
||||||
|
Card readers and cashless upgrades are often part of the decision.
|
||||||
|
</h2>
|
||||||
|
<p className="mt-3 text-sm leading-6 text-muted-foreground md:text-base">
|
||||||
|
If you are trying to modernize how people pay, we can help
|
||||||
|
you think through card readers, mobile payments, and
|
||||||
|
compatibility before you commit to a machine.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="relative order-1 min-h-[260px] md:order-2">
|
||||||
|
<Image
|
||||||
|
src={paymentImage}
|
||||||
|
alt="Cashless payment hardware for vending machines"
|
||||||
|
fill
|
||||||
|
className="object-cover"
|
||||||
|
sizes="(max-width: 768px) 100vw, 360px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PublicSurface>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mt-12">
|
||||||
|
<PublicSectionHeader
|
||||||
|
eyebrow="Buying Process"
|
||||||
|
title="A simpler way to move from questions to a real option"
|
||||||
|
description="You do not need to have the exact model picked out before you reach out. Most of the work is narrowing to the right fit."
|
||||||
|
className="mx-auto mb-8 max-w-3xl text-center"
|
||||||
|
/>
|
||||||
|
<div className="grid gap-4 lg:grid-cols-3">
|
||||||
|
{buyingSteps.map((step, index) => (
|
||||||
|
<PublicInset key={step.title} className="h-full p-5 md:p-6">
|
||||||
|
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-primary text-lg font-semibold text-primary-foreground">
|
||||||
|
{index + 1}
|
||||||
|
</div>
|
||||||
|
<h3 className="mt-4 text-xl font-semibold tracking-tight text-foreground">
|
||||||
|
{step.title}
|
||||||
|
</h3>
|
||||||
|
<p className="mt-3 text-sm leading-6 text-muted-foreground md:text-base">
|
||||||
|
{step.body}
|
||||||
|
</p>
|
||||||
|
</PublicInset>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section className="mt-12 grid gap-6 lg:grid-cols-[0.95fr_1.05fr]">
|
<section className="mt-12 grid gap-6 lg:grid-cols-[0.95fr_1.05fr]">
|
||||||
<PublicSurface>
|
<PublicSurface>
|
||||||
|
|
@ -111,9 +321,9 @@ export default async function MachinesForSalePage() {
|
||||||
Need a free machine instead of buying one?
|
Need a free machine instead of buying one?
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-3 text-base leading-relaxed text-muted-foreground">
|
<p className="mt-3 text-base leading-relaxed text-muted-foreground">
|
||||||
If you're a business looking for placement instead of a
|
If you are a business looking for placement instead of a
|
||||||
purchase, we can help you find the right setup for your
|
purchase, we can help you figure out whether your location is a
|
||||||
location.
|
fit before you spend money on equipment.
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
<GetFreeMachineCta buttonLabel="Get Free Placement" />
|
<GetFreeMachineCta buttonLabel="Get Free Placement" />
|
||||||
|
|
@ -125,13 +335,23 @@ export default async function MachinesForSalePage() {
|
||||||
Need Sales Help?
|
Need Sales Help?
|
||||||
</p>
|
</p>
|
||||||
<h3 className="mt-3 text-2xl font-semibold tracking-tight text-balance">
|
<h3 className="mt-3 text-2xl font-semibold tracking-tight text-balance">
|
||||||
Talk through machine sales, placement, or feature questions.
|
Talk through machine sales, placement, or payment questions.
|
||||||
</h3>
|
</h3>
|
||||||
<p className="mt-3 text-sm leading-relaxed text-muted-foreground">
|
<p className="mt-3 text-sm leading-relaxed text-muted-foreground">
|
||||||
We can help with new vs. used options, payment hardware, and
|
We can help with new vs. used options, payment hardware, and
|
||||||
whether free placement or a direct purchase makes more sense
|
whether free placement or a direct purchase makes more sense
|
||||||
for your location.
|
for your location.
|
||||||
</p>
|
</p>
|
||||||
|
<div className="mt-6">
|
||||||
|
<Button
|
||||||
|
asChild
|
||||||
|
size="lg"
|
||||||
|
variant="outline"
|
||||||
|
className="min-h-11 rounded-full px-6"
|
||||||
|
>
|
||||||
|
<Link href="/contact-us#contact-form">Talk to Sales</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</PublicSurface>
|
</PublicSurface>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import {
|
||||||
|
|
||||||
export function AboutPage() {
|
export function AboutPage() {
|
||||||
return (
|
return (
|
||||||
<div className="public-page max-w-6xl">
|
<div className="public-page">
|
||||||
<Breadcrumbs
|
<Breadcrumbs
|
||||||
className="mb-6"
|
className="mb-6"
|
||||||
items={[{ label: "About Us", href: "/about-us" }]}
|
items={[{ label: "About Us", href: "/about-us" }]}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,37 @@ export function ContactPage() {
|
||||||
description="Use the form for repairs, moving, manuals, machine sales, or general questions. If you'd rather talk now, call us during business hours."
|
description="Use the form for repairs, moving, manuals, machine sales, or general questions. If you'd rather talk now, call us during business hours."
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="mt-10 grid gap-8 lg:grid-cols-[minmax(0,1.15fr)_minmax(320px,0.85fr)] lg:items-start">
|
<section className="mt-10 grid gap-4 lg:grid-cols-3">
|
||||||
|
<PublicInset className="h-full p-5">
|
||||||
|
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-primary/80">
|
||||||
|
Repairs
|
||||||
|
</p>
|
||||||
|
<p className="mt-3 text-sm leading-relaxed text-muted-foreground">
|
||||||
|
Include the machine model, what the machine is doing, and any photos
|
||||||
|
or videos that can help us triage the issue faster.
|
||||||
|
</p>
|
||||||
|
</PublicInset>
|
||||||
|
<PublicInset className="h-full p-5">
|
||||||
|
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-primary/80">
|
||||||
|
Sales or Placement
|
||||||
|
</p>
|
||||||
|
<p className="mt-3 text-sm leading-relaxed text-muted-foreground">
|
||||||
|
Tell us about your location, traffic, and whether you are asking
|
||||||
|
about free placement, machine sales, or both.
|
||||||
|
</p>
|
||||||
|
</PublicInset>
|
||||||
|
<PublicInset className="h-full p-5">
|
||||||
|
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-primary/80">
|
||||||
|
Manuals or Parts
|
||||||
|
</p>
|
||||||
|
<p className="mt-3 text-sm leading-relaxed text-muted-foreground">
|
||||||
|
Share the machine brand and model so we can point you toward the
|
||||||
|
right part, manual, or support path.
|
||||||
|
</p>
|
||||||
|
</PublicInset>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div className="mt-6 grid gap-6 lg:grid-cols-[minmax(0,1.1fr)_minmax(320px,0.9fr)] lg:items-start">
|
||||||
<PublicSurface id="contact-form" as="section" className="p-5 md:p-7">
|
<PublicSurface id="contact-form" as="section" className="p-5 md:p-7">
|
||||||
<div className="mb-6 flex flex-wrap items-center gap-3">
|
<div className="mb-6 flex flex-wrap items-center gap-3">
|
||||||
<div className="rounded-full bg-primary/10 px-3 py-1 text-xs font-semibold uppercase tracking-[0.16em] text-primary">
|
<div className="rounded-full bg-primary/10 px-3 py-1 text-xs font-semibold uppercase tracking-[0.16em] text-primary">
|
||||||
|
|
@ -45,7 +75,21 @@ export function ContactPage() {
|
||||||
/>
|
/>
|
||||||
</PublicSurface>
|
</PublicSurface>
|
||||||
|
|
||||||
<aside className="space-y-5">
|
<aside className="space-y-4 lg:sticky lg:top-28">
|
||||||
|
<PublicSurface className="p-6">
|
||||||
|
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-primary/80">
|
||||||
|
Quick Guidance
|
||||||
|
</p>
|
||||||
|
<h2 className="mt-2 text-2xl font-semibold text-foreground">
|
||||||
|
We'll route you to the right next step
|
||||||
|
</h2>
|
||||||
|
<p className="mt-2 text-sm leading-relaxed text-muted-foreground">
|
||||||
|
If you are not sure whether to ask about placement, repairs,
|
||||||
|
moving, manuals, or sales, that's fine. Send the details you
|
||||||
|
have and we'll help sort it out.
|
||||||
|
</p>
|
||||||
|
</PublicSurface>
|
||||||
|
|
||||||
<PublicSurface className="overflow-hidden p-6">
|
<PublicSurface className="overflow-hidden p-6">
|
||||||
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-primary/80">
|
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-primary/80">
|
||||||
Direct Options
|
Direct Options
|
||||||
|
|
@ -59,10 +103,10 @@ export function ContactPage() {
|
||||||
below.
|
below.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="mt-6 space-y-4">
|
<div className="mt-5 space-y-3">
|
||||||
<a
|
<a
|
||||||
href={businessConfig.publicCallUrl}
|
href={businessConfig.publicCallUrl}
|
||||||
className="flex items-start gap-4 rounded-2xl border border-border/60 bg-white px-4 py-4 transition hover:border-primary/35"
|
className="flex items-start gap-4 rounded-2xl border border-border/55 bg-background/65 px-4 py-4 transition hover:border-primary/35"
|
||||||
>
|
>
|
||||||
<div className="flex h-11 w-11 items-center justify-center rounded-full bg-primary/10 text-primary">
|
<div className="flex h-11 w-11 items-center justify-center rounded-full bg-primary/10 text-primary">
|
||||||
<Phone className="h-5 w-5" />
|
<Phone className="h-5 w-5" />
|
||||||
|
|
@ -80,7 +124,7 @@ export function ContactPage() {
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href={`mailto:${businessConfig.email}?Subject=Rocky%20Mountain%20Vending%20Inquiry`}
|
href={`mailto:${businessConfig.email}?Subject=Rocky%20Mountain%20Vending%20Inquiry`}
|
||||||
className="flex items-start gap-4 rounded-2xl border border-border/60 bg-white px-4 py-4 transition hover:border-primary/35"
|
className="flex items-start gap-4 rounded-2xl border border-border/55 bg-background/65 px-4 py-4 transition hover:border-primary/35"
|
||||||
>
|
>
|
||||||
<div className="flex h-11 w-11 items-center justify-center rounded-full bg-primary/10 text-primary">
|
<div className="flex h-11 w-11 items-center justify-center rounded-full bg-primary/10 text-primary">
|
||||||
<Mail className="h-5 w-5" />
|
<Mail className="h-5 w-5" />
|
||||||
|
|
@ -113,7 +157,7 @@ export function ContactPage() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-5 space-y-2">
|
<div className="mt-4 space-y-2">
|
||||||
{businessHours.map((schedule) => (
|
{businessHours.map((schedule) => (
|
||||||
<PublicInset
|
<PublicInset
|
||||||
key={schedule.day}
|
key={schedule.day}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ interface DropdownPageShellProps {
|
||||||
title: string
|
title: string
|
||||||
description?: string
|
description?: string
|
||||||
headerContent?: ReactNode
|
headerContent?: ReactNode
|
||||||
|
contentIntro?: ReactNode
|
||||||
content: ReactNode
|
content: ReactNode
|
||||||
contentClassName?: string
|
contentClassName?: string
|
||||||
contentSurfaceClassName?: string
|
contentSurfaceClassName?: string
|
||||||
|
|
@ -41,6 +42,7 @@ export function DropdownPageShell({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
headerContent,
|
headerContent,
|
||||||
|
contentIntro,
|
||||||
content,
|
content,
|
||||||
contentClassName,
|
contentClassName,
|
||||||
contentSurfaceClassName,
|
contentSurfaceClassName,
|
||||||
|
|
@ -61,11 +63,32 @@ export function DropdownPageShell({
|
||||||
{headerContent}
|
{headerContent}
|
||||||
</PublicPageHeader>
|
</PublicPageHeader>
|
||||||
|
|
||||||
<section className="mt-10">
|
{contentIntro ? (
|
||||||
|
<section className="mt-10 grid gap-4 lg:grid-cols-2">{contentIntro}</section>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<section className={cn(contentIntro ? "mt-6" : "mt-10")}>
|
||||||
<PublicSurface
|
<PublicSurface
|
||||||
className={cn("overflow-hidden", contentSurfaceClassName)}
|
className={cn(
|
||||||
|
"relative overflow-hidden p-0 md:p-0",
|
||||||
|
contentSurfaceClassName
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<div className={cn("max-w-none", contentClassName)}>{content}</div>
|
<div className="absolute inset-x-0 top-0 h-18 bg-[radial-gradient(circle_at_top,rgba(41,160,71,0.11),transparent_72%)]" />
|
||||||
|
<div className="relative p-5 md:p-7 lg:p-9">
|
||||||
|
<div className="mb-6 flex items-center justify-between gap-4 border-b border-border/55 pb-4">
|
||||||
|
<div>
|
||||||
|
<p className="text-[0.72rem] font-semibold uppercase tracking-[0.22em] text-primary/80">
|
||||||
|
Location Guide
|
||||||
|
</p>
|
||||||
|
<p className="mt-2 max-w-2xl text-sm leading-6 text-muted-foreground">
|
||||||
|
How Rocky Mountain Vending typically approaches this type of
|
||||||
|
location, from placement fit to service expectations.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={cn("mx-auto max-w-3xl", contentClassName)}>{content}</div>
|
||||||
|
</div>
|
||||||
</PublicSurface>
|
</PublicSurface>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
@ -73,7 +96,8 @@ export function DropdownPageShell({
|
||||||
|
|
||||||
{cta ? (
|
{cta ? (
|
||||||
<section className="mt-12">
|
<section className="mt-12">
|
||||||
<PublicSurface className="text-center">
|
<PublicSurface className="overflow-hidden text-center">
|
||||||
|
<div className="absolute inset-x-0 top-0 h-20 bg-[radial-gradient(circle_at_top,rgba(41,160,71,0.10),transparent_70%)]" />
|
||||||
{cta.eyebrow ? (
|
{cta.eyebrow ? (
|
||||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-primary/80">
|
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-primary/80">
|
||||||
{cta.eyebrow}
|
{cta.eyebrow}
|
||||||
|
|
@ -99,7 +123,7 @@ export function DropdownPageShell({
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{cta.note ? (
|
{cta.note ? (
|
||||||
<PublicInset className="mx-auto mt-6 max-w-2xl text-left sm:text-center">
|
<PublicInset className="mx-auto mt-6 max-w-2xl border-primary/10 text-left sm:text-center">
|
||||||
{cta.note}
|
{cta.note}
|
||||||
</PublicInset>
|
</PublicInset>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,15 @@ const features = [
|
||||||
title: "Free Placement",
|
title: "Free Placement",
|
||||||
description:
|
description:
|
||||||
"For qualifying locations, we can place the machines, stock them, and stay responsible for day-to-day service after install.",
|
"For qualifying locations, we can place the machines, stock them, and stay responsible for day-to-day service after install.",
|
||||||
link: "/about-us",
|
link: "/#how-it-works",
|
||||||
linkText: "How placement works",
|
linkText: "How placement works",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: Wrench,
|
icon: Wrench,
|
||||||
title: "Repairs and Service",
|
title: "Repairs and Services",
|
||||||
description:
|
description:
|
||||||
"We handle repairs, restocking, and routine service so your team does not have to manage the machines.",
|
"We handle repairs, restocking, and routine service so your team does not have to manage the machines.",
|
||||||
link: "/services/repairs",
|
link: "/services/repairs#request-service",
|
||||||
linkText: "Repair services",
|
linkText: "Repair services",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export function Footer() {
|
||||||
<div className="mx-auto w-full max-w-[var(--public-shell-max)] px-4 py-14 sm:px-5 md:py-20 lg:px-6">
|
<div className="mx-auto w-full max-w-[var(--public-shell-max)] px-4 py-14 sm:px-5 md:py-20 lg:px-6">
|
||||||
<div className="grid gap-6 lg:grid-cols-[1.2fr_0.9fr_0.9fr_1fr]">
|
<div className="grid gap-6 lg:grid-cols-[1.2fr_0.9fr_0.9fr_1fr]">
|
||||||
{/* Company Info */}
|
{/* Company Info */}
|
||||||
<div className="rounded-[2rem] border border-border/60 bg-white/92 p-6 shadow-[0_18px_48px_rgba(15,23,42,0.07)] lg:col-span-1">
|
<div className="px-6 py-5 lg:col-span-1">
|
||||||
<Link href="/" className="inline-flex">
|
<Link href="/" className="inline-flex">
|
||||||
<Image
|
<Image
|
||||||
src="/rmv-logo.png"
|
src="/rmv-logo.png"
|
||||||
|
|
@ -104,7 +104,7 @@ export function Footer() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Services */}
|
{/* Services */}
|
||||||
<div className="footer-section rounded-[2rem] border border-border/60 bg-white/88 px-5 py-5 shadow-[0_14px_38px_rgba(15,23,42,0.06)]">
|
<div className="px-5 py-5">
|
||||||
<h3 className="font-semibold mb-5 text-base">Services</h3>
|
<h3 className="font-semibold mb-5 text-base">Services</h3>
|
||||||
<ul className="space-y-3 text-sm text-muted-foreground">
|
<ul className="space-y-3 text-sm text-muted-foreground">
|
||||||
<li>
|
<li>
|
||||||
|
|
@ -159,7 +159,7 @@ export function Footer() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Company */}
|
{/* Company */}
|
||||||
<div className="footer-section rounded-[2rem] border border-border/60 bg-white/88 px-5 py-5 shadow-[0_14px_38px_rgba(15,23,42,0.06)]">
|
<div className="px-5 py-5">
|
||||||
<h3 className="font-semibold mb-5 text-base">Company</h3>
|
<h3 className="font-semibold mb-5 text-base">Company</h3>
|
||||||
<ul className="space-y-3 text-sm text-muted-foreground">
|
<ul className="space-y-3 text-sm text-muted-foreground">
|
||||||
<li>
|
<li>
|
||||||
|
|
@ -198,7 +198,7 @@ export function Footer() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Service Areas */}
|
{/* Service Areas */}
|
||||||
<div className="footer-section rounded-[2rem] border border-border/60 bg-white/88 px-5 py-5 shadow-[0_14px_38px_rgba(15,23,42,0.06)]">
|
<div className="px-5 py-5">
|
||||||
<h3 className="font-semibold mb-5 text-base">Service Areas</h3>
|
<h3 className="font-semibold mb-5 text-base">Service Areas</h3>
|
||||||
<ul className="space-y-3 text-sm text-muted-foreground">
|
<ul className="space-y-3 text-sm text-muted-foreground">
|
||||||
<li>
|
<li>
|
||||||
|
|
|
||||||
|
|
@ -162,9 +162,16 @@ export function Header() {
|
||||||
{ label: "Reviews", href: "/reviews" },
|
{ label: "Reviews", href: "/reviews" },
|
||||||
{ label: "FAQs", href: "/about/faqs" },
|
{ label: "FAQs", href: "/about/faqs" },
|
||||||
]
|
]
|
||||||
|
const moreItems = [
|
||||||
|
{ label: "Food & Beverage", href: "/food-and-beverage/healthy-options" },
|
||||||
|
{ label: "Blog Posts", href: "/blog" },
|
||||||
|
{ label: "About Us", href: "/about-us" },
|
||||||
|
{ label: "Products", href: "/products" },
|
||||||
|
{ label: "Service Areas", href: "/service-areas" },
|
||||||
|
]
|
||||||
|
|
||||||
const desktopLinkClassName =
|
const desktopLinkClassName =
|
||||||
"rounded-full px-3 py-2 text-sm font-medium text-foreground transition hover:text-primary focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-primary/15"
|
"inline-flex items-center whitespace-nowrap rounded-full px-2.5 py-2 text-[0.95rem] font-medium text-foreground transition hover:text-primary focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-primary/15 lg:px-3 lg:text-sm"
|
||||||
const mobileLinkClassName =
|
const mobileLinkClassName =
|
||||||
"flex min-h-11 items-center rounded-[1rem] px-4 text-sm font-medium text-foreground transition hover:bg-primary/6 hover:text-primary focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-primary/15"
|
"flex min-h-11 items-center rounded-[1rem] px-4 text-sm font-medium text-foreground transition hover:bg-primary/6 hover:text-primary focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-primary/15"
|
||||||
const mobileGroupButtonClassName =
|
const mobileGroupButtonClassName =
|
||||||
|
|
@ -175,7 +182,7 @@ export function Header() {
|
||||||
return (
|
return (
|
||||||
<header className="sticky top-0 z-40 w-full border-b border-border/50 bg-white/92 shadow-[0_10px_35px_rgba(15,23,42,0.06)] backdrop-blur supports-[backdrop-filter]:bg-white/80">
|
<header className="sticky top-0 z-40 w-full border-b border-border/50 bg-white/92 shadow-[0_10px_35px_rgba(15,23,42,0.06)] backdrop-blur supports-[backdrop-filter]:bg-white/80">
|
||||||
<div className="mx-auto w-full max-w-[var(--public-shell-max)] px-4 sm:px-5 lg:px-6">
|
<div className="mx-auto w-full max-w-[var(--public-shell-max)] px-4 sm:px-5 lg:px-6">
|
||||||
<div className="flex h-[var(--header-height)] items-center justify-between gap-3 lg:gap-6">
|
<div className="flex h-[var(--header-height)] items-center justify-between gap-3 md:hidden lg:gap-6">
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<Link
|
<Link
|
||||||
href="/"
|
href="/"
|
||||||
|
|
@ -192,7 +199,7 @@ export function Header() {
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{/* Desktop Navigation */}
|
{/* Desktop Navigation */}
|
||||||
<nav className="hidden flex-1 items-center justify-center gap-1 md:flex lg:gap-2">
|
<nav className="hidden flex-1 items-center justify-center gap-1 2xl:flex 2xl:gap-2">
|
||||||
<Link href="/" className={desktopLinkClassName}>
|
<Link href="/" className={desktopLinkClassName}>
|
||||||
Home
|
Home
|
||||||
</Link>
|
</Link>
|
||||||
|
|
@ -371,7 +378,7 @@ export function Header() {
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Desktop CTA */}
|
{/* Desktop CTA */}
|
||||||
<div className="hidden flex-shrink-0 items-center gap-2 md:flex lg:gap-3">
|
<div className="hidden flex-shrink-0 items-center gap-2 2xl:flex 2xl:gap-3">
|
||||||
<CartButton
|
<CartButton
|
||||||
onClick={() => dispatch({ type: "SET_CART", value: true })}
|
onClick={() => dispatch({ type: "SET_CART", value: true })}
|
||||||
/>
|
/>
|
||||||
|
|
@ -393,7 +400,7 @@ export function Header() {
|
||||||
|
|
||||||
{/* Mobile Menu Button */}
|
{/* Mobile Menu Button */}
|
||||||
<button
|
<button
|
||||||
className="inline-flex h-11 w-11 items-center justify-center rounded-full border border-border/60 bg-white text-foreground transition hover:border-primary/35 hover:text-primary focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-primary/15 md:hidden"
|
className="inline-flex h-11 w-11 items-center justify-center rounded-full border border-border/60 bg-white text-foreground transition hover:border-primary/35 hover:text-primary focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-primary/15 2xl:hidden"
|
||||||
onClick={() => dispatch({ type: "TOGGLE_MENU" })}
|
onClick={() => dispatch({ type: "TOGGLE_MENU" })}
|
||||||
aria-label="Toggle menu"
|
aria-label="Toggle menu"
|
||||||
aria-expanded={state.isMenuOpen}
|
aria-expanded={state.isMenuOpen}
|
||||||
|
|
@ -406,6 +413,161 @@ export function Header() {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="hidden md:block">
|
||||||
|
<div className="flex min-h-[4.75rem] items-center justify-between gap-4 py-3">
|
||||||
|
<Link
|
||||||
|
href="/"
|
||||||
|
className="flex min-w-0 flex-shrink-0 items-center gap-2 rounded-full"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src="/rmv-logo.png"
|
||||||
|
alt="Rocky Mountain Vending"
|
||||||
|
width={220}
|
||||||
|
height={55}
|
||||||
|
className="h-12 w-auto object-contain lg:h-14"
|
||||||
|
priority
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<div className="flex min-w-0 flex-shrink-0 items-center gap-2 lg:gap-3">
|
||||||
|
<CartButton
|
||||||
|
onClick={() => dispatch({ type: "SET_CART", value: true })}
|
||||||
|
/>
|
||||||
|
<a
|
||||||
|
href="tel:+14352339668"
|
||||||
|
className="inline-flex min-h-10 items-center gap-2 rounded-full border border-border/60 bg-white px-3 text-sm font-medium text-foreground transition hover:border-primary/35 hover:text-primary lg:px-4"
|
||||||
|
>
|
||||||
|
<Phone className="h-4 w-4 flex-shrink-0" />
|
||||||
|
<span className="hidden xl:inline">(435) 233-9668</span>
|
||||||
|
<span className="xl:hidden">Call</span>
|
||||||
|
</a>
|
||||||
|
<Button
|
||||||
|
onClick={() => dispatch({ type: "SET_MODAL", value: true })}
|
||||||
|
className="h-10 whitespace-nowrap rounded-full bg-primary px-4 text-sm hover:bg-primary/90 lg:px-5"
|
||||||
|
size="lg"
|
||||||
|
>
|
||||||
|
Get Free Machine
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex min-h-[3.5rem] items-center justify-center border-t border-border/45 py-2">
|
||||||
|
<nav className="flex flex-wrap items-center justify-center gap-x-1 gap-y-2 lg:gap-x-2">
|
||||||
|
<Link href="/" className={desktopLinkClassName}>
|
||||||
|
Home
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger
|
||||||
|
className={cn(
|
||||||
|
desktopLinkClassName,
|
||||||
|
"gap-1.5 data-[state=open]:text-primary"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Who We Serve
|
||||||
|
<ChevronDown className="h-4 w-4" aria-hidden="true" />
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent
|
||||||
|
align="start"
|
||||||
|
className={cn("w-56", desktopDropdownClassName)}
|
||||||
|
>
|
||||||
|
{whoWeServeItems.map((item) => (
|
||||||
|
<DropdownMenuItem
|
||||||
|
key={item.href}
|
||||||
|
asChild
|
||||||
|
className="rounded-xl px-3 py-2.5"
|
||||||
|
>
|
||||||
|
<Link href={item.href}>{item.label}</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
))}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger
|
||||||
|
className={cn(
|
||||||
|
desktopLinkClassName,
|
||||||
|
"gap-1.5 data-[state=open]:text-primary"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Vending Machines
|
||||||
|
<ChevronDown className="h-4 w-4" aria-hidden="true" />
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent
|
||||||
|
align="start"
|
||||||
|
className={cn("w-64", desktopDropdownClassName)}
|
||||||
|
>
|
||||||
|
{vendingMachinesItems.map((item) => (
|
||||||
|
<DropdownMenuItem
|
||||||
|
key={item.href}
|
||||||
|
asChild
|
||||||
|
className="rounded-xl px-3 py-2.5"
|
||||||
|
>
|
||||||
|
<Link href={item.href}>{item.label}</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
))}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger
|
||||||
|
className={cn(
|
||||||
|
desktopLinkClassName,
|
||||||
|
"gap-1.5 data-[state=open]:text-primary"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Services
|
||||||
|
<ChevronDown className="h-4 w-4" aria-hidden="true" />
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent
|
||||||
|
align="start"
|
||||||
|
className={cn("w-72", desktopDropdownClassName)}
|
||||||
|
>
|
||||||
|
{servicesItems.map((item) => (
|
||||||
|
<DropdownMenuItem
|
||||||
|
key={item.href}
|
||||||
|
asChild
|
||||||
|
className="rounded-xl px-3 py-2.5"
|
||||||
|
>
|
||||||
|
<Link href={item.href}>{item.label}</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
))}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
|
||||||
|
<Link href="/contact-us" className={desktopLinkClassName}>
|
||||||
|
Contact Us
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger
|
||||||
|
className={cn(
|
||||||
|
desktopLinkClassName,
|
||||||
|
"gap-1.5 data-[state=open]:text-primary"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
More
|
||||||
|
<ChevronDown className="h-4 w-4" aria-hidden="true" />
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent
|
||||||
|
align="end"
|
||||||
|
className={cn("w-56", desktopDropdownClassName)}
|
||||||
|
>
|
||||||
|
{moreItems.map((item) => (
|
||||||
|
<DropdownMenuItem
|
||||||
|
key={item.href}
|
||||||
|
asChild
|
||||||
|
className="rounded-xl px-3 py-2.5"
|
||||||
|
>
|
||||||
|
<Link href={item.href}>{item.label}</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
))}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Mobile Navigation */}
|
{/* Mobile Navigation */}
|
||||||
{state.isMenuOpen && (
|
{state.isMenuOpen && (
|
||||||
<nav className="border-t border-border/40 py-5 md:hidden">
|
<nav className="border-t border-border/40 py-5 md:hidden">
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ export function PublicSurface({
|
||||||
return (
|
return (
|
||||||
<Component
|
<Component
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-[var(--public-surface-radius)] border border-border/70 bg-[linear-gradient(180deg,rgba(255,255,255,0.98),rgba(255,251,243,0.96))] p-5 shadow-[var(--public-surface-shadow)] md:p-7",
|
"rounded-[var(--public-surface-radius)] border border-border/65 bg-[linear-gradient(180deg,rgba(255,255,255,0.98),rgba(255,249,240,0.96))] p-5 shadow-[0_20px_52px_rgba(15,23,42,0.075)] md:p-7",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -120,7 +120,7 @@ export function PublicInset({
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-[var(--public-inset-radius)] border border-border/60 bg-white/95 p-4 shadow-[0_10px_28px_rgba(15,23,42,0.06)]",
|
"rounded-[var(--public-inset-radius)] border border-border/55 bg-[linear-gradient(180deg,rgba(255,255,255,0.94),rgba(255,250,244,0.92))] p-4 shadow-[0_12px_30px_rgba(15,23,42,0.055)]",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -144,14 +144,16 @@ export function PublicSectionHeader({
|
||||||
className,
|
className,
|
||||||
}: PublicSectionHeaderProps) {
|
}: PublicSectionHeaderProps) {
|
||||||
return (
|
return (
|
||||||
<div className={cn("space-y-2", className)}>
|
<div className={cn("space-y-2.5", className)}>
|
||||||
<p className="text-[0.72rem] font-semibold uppercase tracking-[0.22em] text-primary/80">
|
<p className="text-[0.72rem] font-semibold uppercase tracking-[0.22em] text-primary/80">
|
||||||
{eyebrow}
|
{eyebrow}
|
||||||
</p>
|
</p>
|
||||||
<h2 className="text-xl font-semibold tracking-tight text-foreground md:text-[1.375rem]">
|
<h2 className="text-xl font-semibold tracking-tight text-foreground md:text-[1.375rem]">
|
||||||
{title}
|
{title}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm leading-6 text-muted-foreground">{description}</p>
|
<p className="max-w-2xl text-sm leading-6 text-muted-foreground">
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ export function RequestMachineSection() {
|
||||||
<section id="request-machine" className="bg-background py-16 md:py-24">
|
<section id="request-machine" className="bg-background py-16 md:py-24">
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
<div className="grid gap-8 lg:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)] lg:items-start">
|
<div className="grid gap-8 lg:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)] lg:items-start">
|
||||||
<PublicSurface className="bg-white p-6 md:p-8 lg:sticky lg:top-28">
|
<PublicSurface className="p-6 md:p-8 lg:sticky lg:top-28">
|
||||||
<div className="inline-flex items-center gap-2 rounded-full bg-primary/10 px-3 py-1 text-xs font-semibold uppercase tracking-[0.16em] text-primary">
|
<div className="inline-flex items-center gap-2 rounded-full bg-primary/10 px-3 py-1 text-xs font-semibold uppercase tracking-[0.16em] text-primary">
|
||||||
<Package className="h-4 w-4" />
|
<Package className="h-4 w-4" />
|
||||||
Free Placement
|
Free Placement
|
||||||
|
|
@ -61,7 +61,7 @@ export function RequestMachineSection() {
|
||||||
</div>
|
</div>
|
||||||
</PublicSurface>
|
</PublicSurface>
|
||||||
|
|
||||||
<PublicSurface className="bg-white p-5 md:p-7">
|
<PublicSurface className="p-5 md:p-7">
|
||||||
<RequestMachineForm
|
<RequestMachineForm
|
||||||
onSubmit={(data) =>
|
onSubmit={(data) =>
|
||||||
console.log("Machine request form submitted:", data)
|
console.log("Machine request form submitted:", data)
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ export function ReviewsPage() {
|
||||||
|
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
<iframe
|
<iframe
|
||||||
className="lc_reviews_widget min-h-[900px] w-full rounded-[1.5rem] border border-border/60 bg-background"
|
className="lc_reviews_widget min-h-[780px] w-full rounded-[1.5rem] border border-border/60 bg-background"
|
||||||
src="https://reputationhub.site/reputation/widgets/review_widget/YAoWLgNSid8oG44j9BjG"
|
src="https://reputationhub.site/reputation/widgets/review_widget/YAoWLgNSid8oG44j9BjG"
|
||||||
frameBorder="0"
|
frameBorder="0"
|
||||||
scrolling="no"
|
scrolling="no"
|
||||||
|
|
@ -81,7 +81,7 @@ export function ReviewsPage() {
|
||||||
</PublicSurface>
|
</PublicSurface>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="mt-12 grid gap-6 lg:grid-cols-[1.15fr_0.85fr]">
|
<section className="mt-12 grid gap-6 lg:grid-cols-[1.05fr_0.95fr]">
|
||||||
<PublicSurface>
|
<PublicSurface>
|
||||||
<h2 className="text-3xl font-semibold tracking-tight text-balance">
|
<h2 className="text-3xl font-semibold tracking-tight text-balance">
|
||||||
What businesses usually want to verify before they choose a vendor
|
What businesses usually want to verify before they choose a vendor
|
||||||
|
|
@ -120,21 +120,32 @@ export function ReviewsPage() {
|
||||||
<PublicSurface className="flex flex-col justify-between">
|
<PublicSurface className="flex flex-col justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-primary/80">
|
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-primary/80">
|
||||||
Next Step
|
Why It Matters
|
||||||
</p>
|
</p>
|
||||||
<h2 className="mt-3 text-3xl font-semibold tracking-tight text-balance">
|
<h2 className="mt-3 text-3xl font-semibold tracking-tight text-balance">
|
||||||
Want to see whether your location qualifies?
|
Reviews are usually the last confidence check before someone reaches out.
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-3 text-base leading-relaxed text-muted-foreground">
|
<p className="mt-3 text-base leading-relaxed text-muted-foreground">
|
||||||
Tell us about your traffic, breakroom, or customer area and
|
Most businesses are trying to verify the same things: follow-through,
|
||||||
we'll help you decide between free placement, machine sales,
|
communication, and whether the machines stay stocked and working
|
||||||
or service help.
|
after install. If that sounds like your checklist too, we can help
|
||||||
|
you sort through next steps quickly.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 grid gap-4 sm:grid-cols-2">
|
<div className="mt-6 grid gap-4">
|
||||||
|
<PublicInset className="p-5">
|
||||||
|
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-primary/80">
|
||||||
|
Common Questions
|
||||||
|
</p>
|
||||||
|
<ul className="mt-3 space-y-3 text-sm leading-relaxed text-muted-foreground">
|
||||||
|
<li>Does this location qualify for free placement?</li>
|
||||||
|
<li>Can Rocky handle repairs and restocking without extra staff work on our side?</li>
|
||||||
|
<li>Should we ask about placement, machine sales, or direct service help?</li>
|
||||||
|
</ul>
|
||||||
|
</PublicInset>
|
||||||
<Link
|
<Link
|
||||||
href="/#request-machine"
|
href="/#request-machine"
|
||||||
className="rounded-[1.5rem] border border-border/60 bg-white p-5 text-left transition hover:border-primary/30 hover:text-primary"
|
className="rounded-[1.5rem] border border-border/55 bg-background/70 p-5 text-left transition hover:border-primary/30 hover:text-primary"
|
||||||
>
|
>
|
||||||
<h3 className="text-lg font-semibold text-foreground">
|
<h3 className="text-lg font-semibold text-foreground">
|
||||||
Free Placement
|
Free Placement
|
||||||
|
|
@ -146,7 +157,7 @@ export function ReviewsPage() {
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="/contact-us#contact-form"
|
href="/contact-us#contact-form"
|
||||||
className="rounded-[1.5rem] border border-border/60 bg-white p-5 text-left transition hover:border-primary/30 hover:text-primary"
|
className="rounded-[1.5rem] border border-border/55 bg-background/70 p-5 text-left transition hover:border-primary/30 hover:text-primary"
|
||||||
>
|
>
|
||||||
<h3 className="text-lg font-semibold text-foreground">
|
<h3 className="text-lg font-semibold text-foreground">
|
||||||
Service or Sales
|
Service or Sales
|
||||||
|
|
@ -157,6 +168,16 @@ export function ReviewsPage() {
|
||||||
</p>
|
</p>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="mt-6 rounded-[1.5rem] border border-primary/12 bg-[linear-gradient(180deg,rgba(41,160,71,0.06),rgba(255,255,255,0.7))] p-5">
|
||||||
|
<p className="text-sm font-semibold text-foreground">
|
||||||
|
Looking for a direct answer?
|
||||||
|
</p>
|
||||||
|
<p className="mt-2 text-sm leading-relaxed text-muted-foreground">
|
||||||
|
Tell us about your location, traffic, and what kind of help you
|
||||||
|
need. We'll point you toward the right option instead of
|
||||||
|
making you guess between service pages.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</PublicSurface>
|
</PublicSurface>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import { ReactNode } from "react"
|
||||||
import { CheckCircle2 } from "lucide-react"
|
import { CheckCircle2 } from "lucide-react"
|
||||||
import { DropdownPageShell } from "@/components/dropdown-page-shell"
|
import { DropdownPageShell } from "@/components/dropdown-page-shell"
|
||||||
import { PublicInset } from "@/components/public-surface"
|
import { PublicInset } from "@/components/public-surface"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
interface WhoWeServePageProps {
|
interface WhoWeServePageProps {
|
||||||
title: string
|
title: string
|
||||||
|
|
@ -55,6 +57,51 @@ export function WhoWeServePage({
|
||||||
description ||
|
description ||
|
||||||
"See how Rocky Mountain Vending adapts machine placement, product mix, and ongoing service to the way this kind of location actually runs."
|
"See how Rocky Mountain Vending adapts machine placement, product mix, and ongoing service to the way this kind of location actually runs."
|
||||||
}
|
}
|
||||||
|
headerContent={
|
||||||
|
<div className="flex flex-col items-center justify-center gap-3 sm:flex-row">
|
||||||
|
<Button asChild size="lg" className="min-h-11 rounded-full px-6">
|
||||||
|
<Link href="/contact-us#contact-form">Talk to Our Team</Link>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
asChild
|
||||||
|
size="lg"
|
||||||
|
variant="outline"
|
||||||
|
className="min-h-11 rounded-full px-6"
|
||||||
|
>
|
||||||
|
<Link href="/#request-machine">See If You Qualify</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
contentIntro={
|
||||||
|
<>
|
||||||
|
<PublicInset className="h-full p-5 md:p-6">
|
||||||
|
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-primary/80">
|
||||||
|
How We Tailor Service
|
||||||
|
</p>
|
||||||
|
<h2 className="mt-3 text-2xl font-semibold tracking-tight text-balance text-foreground">
|
||||||
|
We shape the setup around the pace of the location.
|
||||||
|
</h2>
|
||||||
|
<ul className="mt-4 space-y-3 text-sm leading-relaxed text-muted-foreground">
|
||||||
|
<li>Machine type and product mix matched to how people actually use the space.</li>
|
||||||
|
<li>Placement recommendations based on traffic flow, break patterns, and visibility.</li>
|
||||||
|
<li>Service cadence adjusted so stocking and support stay consistent without adding staff work.</li>
|
||||||
|
</ul>
|
||||||
|
</PublicInset>
|
||||||
|
<PublicInset className="h-full p-5 md:p-6">
|
||||||
|
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-primary/80">
|
||||||
|
Good Fit Signals
|
||||||
|
</p>
|
||||||
|
<h2 className="mt-3 text-2xl font-semibold tracking-tight text-balance text-foreground">
|
||||||
|
These are usually the reasons businesses reach out first.
|
||||||
|
</h2>
|
||||||
|
<ul className="mt-4 space-y-3 text-sm leading-relaxed text-muted-foreground">
|
||||||
|
<li>Your team or visitors need easier access to drinks, snacks, or convenience items on site.</li>
|
||||||
|
<li>You want a cleaner vending setup without daily oversight falling back on your staff.</li>
|
||||||
|
<li>You need local follow-through when a machine needs restocking, repair, or payment support.</li>
|
||||||
|
</ul>
|
||||||
|
</PublicInset>
|
||||||
|
</>
|
||||||
|
}
|
||||||
content={
|
content={
|
||||||
<div className="text-foreground">{content}</div>
|
<div className="text-foreground">{content}</div>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue