Greenlens/greenlns-landing/components/BrownLeaf.tsx

350 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import { useRef, useState } from 'react'
import { useLang } from '@/context/LangContext'
export default function BrownLeaf() {
const { t } = useLang()
const bl = t.brownLeaf
const [sliderVal, setSliderVal] = useState(50)
const containerRef = useRef<HTMLDivElement>(null)
const proofs = [
{
icon: (
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#e07a50" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z" />
</svg>
),
title: bl.proof1title,
desc: bl.proof1desc,
color: '#e07a50',
},
{
icon: (
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#56a074" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<path d="M7 16.3c2.2 0 4-1.83 4-4.05 0-1.16-.57-2.26-1.71-3.19S7.29 6.75 7 5.3c-.29 1.45-1.14 2.84-2.29 3.76S3 11.1 3 12.25c0 2.22 1.8 4.05 4 4.05z" />
<path d="M12.56 6.6A10.97 10.97 0 0 0 14 3.02c.5 2.5 2 4.9 4 6.5s3 3.5 3 5.5a6.98 6.98 0 0 1-11.91 4.97" />
</svg>
),
title: bl.proof2title,
desc: bl.proof2desc,
color: '#56a074',
},
{
icon: (
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#3d7a56" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<path d="M9 3h6l-1 7H10z" />
<path d="M12 10v4" />
<path d="M8 21v-4a4 4 0 0 1 8 0v4" />
<path d="M6 21h12" />
<path d="M4 10h3" />
<path d="M17 10h3" />
</svg>
),
title: bl.proof3title,
desc: bl.proof3desc,
color: '#3d7a56',
},
]
return (
<section className="brownleaf" id="brownleaf" aria-labelledby="bl-heading">
<div className="container">
{/* Header */}
<header className="bl-header reveal">
<p className="tag">{bl.tag}</p>
<h2 id="bl-heading">
{bl.headline}<br />
<em>{bl.sub}</em>
</h2>
<p className="bl-desc">{bl.desc}</p>
</header>
{/* Before / After Slider */}
<div className="bl-slider-wrap reveal delay-1" ref={containerRef}>
<div
className="bl-slider-track"
style={{ '--pos': `${sliderVal}%` } as React.CSSProperties}
aria-label={bl.sliderLabel}
>
{/* BEFORE brown/unhealthy (always visible) */}
<div className="bl-panel bl-before" aria-hidden="true">
<div className="bl-panel-img bl-before-img" />
<div className="bl-panel-overlay bl-before-overlay" />
<span className="bl-label bl-label-before">{bl.before}</span>
</div>
{/* AFTER healthy plant (clips from right) */}
<div
className="bl-panel bl-after"
style={{ clipPath: `inset(0 0 0 ${sliderVal}%)` }}
aria-hidden="true"
>
<div className="bl-panel-img bl-after-img" />
<div className="bl-panel-overlay bl-after-overlay" />
<span className="bl-label bl-label-after">{bl.after}</span>
</div>
{/* Divider line */}
<div
className="bl-divider"
style={{ left: `${sliderVal}%` }}
aria-hidden="true"
>
<div className="bl-divider-handle">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
<path d="M9 18l-6-6 6-6" />
<path d="M15 6l6 6-6 6" />
</svg>
</div>
</div>
{/* Range input for accessibility & interaction */}
<input
type="range"
min={0}
max={100}
value={sliderVal}
onChange={e => setSliderVal(Number(e.target.value))}
className="bl-range"
aria-label={bl.sliderLabel}
/>
</div>
{/* Scan badge overlay */}
<div className="bl-scan-badge">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<path d="M9.5 2a2.5 2.5 0 0 1 5 0v.5a2 2 0 0 0 1.5 1.94V6a2 2 0 0 0 2 2h.5a2.5 2.5 0 0 1 0 5H18a2 2 0 0 0-2 2v.56A2.5 2.5 0 0 1 14.5 22h-5A2.5 2.5 0 0 1 7 19.56V18a2 2 0 0 0-2-2h-.5a2.5 2.5 0 0 1 0-5H5a2 2 0 0 0 2-2V4.44A2 2 0 0 0 8.5 2.5z" />
<circle cx="12" cy="12" r="2" />
</svg>
Botanical Intelligence
</div>
</div>
{/* Success Story Cards */}
<div className="bl-proofs">
{proofs.map((p, i) => (
<div className={`bl-proof reveal delay-${i + 1}`} key={p.title}>
<div className="bl-proof-icon" style={{ background: `${p.color}18`, border: `1px solid ${p.color}30` }}>
{p.icon}
</div>
<div>
<h4 className="bl-proof-title">{p.title}</h4>
<p className="bl-proof-desc">{p.desc}</p>
</div>
</div>
))}
</div>
</div>
<style jsx>{`
.brownleaf {
padding: var(--s16) 0;
background: var(--dark);
position: relative;
overflow: hidden;
}
.brownleaf::before {
content: '';
position: absolute;
width: 600px;
height: 600px;
background: radial-gradient(circle, rgba(224,122,80,0.08) 0%, transparent 70%);
top: -100px;
left: -200px;
pointer-events: none;
}
.bl-header {
text-align: center;
max-width: 640px;
margin: 0 auto var(--s12);
}
.bl-header h2 {
color: var(--cream);
margin-bottom: var(--s3);
}
.bl-header h2 em {
display: block;
font-style: italic;
color: var(--green-light);
}
.bl-desc {
color: var(--text-light);
font-size: 1rem;
line-height: 1.75;
}
/* Slider */
.bl-slider-wrap {
position: relative;
max-width: 800px;
margin: 0 auto var(--s12);
}
.bl-slider-track {
position: relative;
border-radius: var(--r-xl);
overflow: hidden;
aspect-ratio: 16/9;
user-select: none;
box-shadow: 0 30px 80px rgba(0,0,0,0.4), 0 0 0 1px rgba(244,241,232,0.06);
cursor: col-resize;
}
.bl-panel {
position: absolute;
inset: 0;
}
.bl-panel-img {
width: 100%;
height: 100%;
background-size: cover;
background-position: center;
}
.bl-before-img {
background-image: url(/unhealthy-plant.png);
}
.bl-after-img {
background-image: url(/plant-collection.png);
filter: saturate(1.3) brightness(1.05);
}
.bl-panel-overlay {
position: absolute;
inset: 0;
}
.bl-before-overlay {
background: linear-gradient(135deg, rgba(80,40,10,0.35) 0%, transparent 60%);
}
.bl-after-overlay {
background: linear-gradient(135deg, rgba(13,40,20,0.2) 0%, transparent 60%);
}
.bl-label {
position: absolute;
bottom: 1.2rem;
font-size: 0.7rem;
font-weight: 800;
letter-spacing: 0.15em;
text-transform: uppercase;
color: rgba(244,241,232,0.9);
background: rgba(0,0,0,0.45);
backdrop-filter: blur(8px);
border-radius: 999px;
padding: 0.3rem 0.8rem;
border: 1px solid rgba(244,241,232,0.12);
}
.bl-label-before { left: 1.2rem; }
.bl-label-after { right: 1.2rem; }
/* Divider */
.bl-divider {
position: absolute;
top: 0;
bottom: 0;
width: 2px;
background: rgba(244,241,232,0.7);
transform: translateX(-50%);
pointer-events: none;
z-index: 10;
}
.bl-divider-handle {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 44px;
height: 44px;
border-radius: 50%;
background: #fff;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
color: var(--dark);
}
/* Range input */
.bl-range {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
opacity: 0;
cursor: col-resize;
z-index: 20;
margin: 0;
}
/* Scan badge */
.bl-scan-badge {
position: absolute;
top: 1.2rem;
right: 1.2rem;
display: flex;
align-items: center;
gap: 0.4rem;
background: rgba(19,31,22,0.85);
backdrop-filter: blur(12px);
border: 1px solid rgba(86,160,116,0.3);
border-radius: var(--r-md);
padding: 0.5rem 0.9rem;
color: var(--green-light);
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.06em;
z-index: 5;
pointer-events: none;
}
/* Proof cards */
.bl-proofs {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--s3);
max-width: 800px;
margin: 0 auto;
}
.bl-proof {
display: flex;
align-items: flex-start;
gap: var(--s2);
background: rgba(244,241,232,0.04);
border: 1px solid rgba(244,241,232,0.08);
border-radius: var(--r-lg);
padding: var(--s3);
}
.bl-proof-icon {
width: 44px;
height: 44px;
min-width: 44px;
border-radius: var(--r-sm);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.3rem;
}
.bl-proof-title {
font-family: var(--body);
font-size: 0.82rem;
font-weight: 700;
color: var(--cream);
margin-bottom: 3px;
}
.bl-proof-desc {
font-size: 0.75rem;
color: var(--text-light);
line-height: 1.5;
}
@media (max-width: 768px) {
.bl-proofs {
grid-template-columns: 1fr;
}
}
`}</style>
</section>
)
}