/* global React, useApp, useReveal */
const { useState, useEffect, useRef } = React;
/* ==========================================================
SECTION: Two wavelengths — SVG cross-section + spectrum + waveform
========================================================== */
function WavelengthsSection() {
const { t } = useApp();
const copy = t.wavelengths;
const [wl, setWl] = useState(850); // 630 or 850
const ref = useReveal();
const is630 = wl === 630;
// Realistic penetration depth as fraction of visible 20mm range:
// 630nm → ~1mm (stops in upper dermis) = 0.20
// 850nm → ~8-10mm (deep into muscle) = 0.78
const depth = is630 ? 0.22 : 0.80;
return (
{/* ambient orbs */}
{copy.eyebrow}
{copy.title}
{copy.subtitle}
{/* LEFT — SKIN CROSS-SECTION */}
{/* live depth badge */}
{copy.scrubLabel}
{is630 ? "~1" : "~8"}
mm
{/* RIGHT — CONTROL PANEL */}
{/* Toggle */}
{[
{v:630, t:copy.redTitle, short:"630 nm"},
{v:850, t:copy.irTitle, short:"850 nm"},
].map(({v, t:lbl, short}) => (
))}
{/* Big number */}
{wl}nm
{is630 ? copy.redDesc : copy.irDesc}
{/* Spectrum bar with animated marker */}
{/* Waveform oscilloscope */}
);
}
/* ------------- SVG skin cross-section ------------- */
function SkinCrossSection({ wl, depth, layerLabels }) {
const is630 = wl === 630;
// Panel band: y 0–70. Skin zones 70–780, label zone 780–800.
const panelH = 70;
const skinStart = panelH;
const skinEnd = 780;
const skinH = skinEnd - skinStart; // 710
// Layers scaled by anatomical proportion (not 1:1 — compressed for readability):
// Naskórek 4%, Skóra wł. 22%, Podskórna 28%, Mięśnie 32%, Stawy 14%
const proportions = [0.04, 0.22, 0.28, 0.32, 0.14];
const layers = [];
let acc = skinStart;
const palettes = [
["#f0d4c0", "#d8a898"], // naskórek
["#c89685", "#8a5e50"], // skóra właściwa
["#e0c090", "#b88860"], // podskórna (fat)
["#8a3a28", "#521c12"], // mięśnie
["#d8c2a2", "#8c7258"], // stawy (bone/cartilage)
];
const mmLabels = ["0.1 mm", "1.5 mm", "5 mm", "15 mm", "30 mm+"];
for (let i=0; i<5; i++) {
const h = proportions[i] * skinH;
layers.push({
name: layerLabels[i],
y1: acc, y2: acc + h,
c1: palettes[i][0], c2: palettes[i][1],
mm: mmLabels[i],
});
acc += h;
}
const beamEndY = skinStart + depth * skinH;
const beamColor = is630 ? "#ff3850" : "#c41528";
const beamCore = is630 ? "#ff6b78" : "#ee2838";
const haloColor = is630 ? "#ff4a57" : "#c41020";
// depth ruler — labeled in mm, mapped to the viewBox
const rulerMarks = [
{ mm: "0", y: skinStart },
{ mm: "1", y: skinStart + 0.04 * skinH },
{ mm: "3", y: skinStart + 0.18 * skinH },
{ mm: "8", y: skinStart + 0.40 * skinH },
{ mm: "20", y: skinStart + 0.75 * skinH },
{ mm: "50+", y: skinEnd },
];
const nbBeams = 7;
const beamXs = [...Array(nbBeams)].map((_, i) => 96 + i * 68);
// cell particles: 630 in upper zones, 850 in muscle depth
const particles = is630
? [...Array(20)].map((_, i) => ({
x: 70 + (i * 29) % 480,
y: skinStart + 0.06 * skinH + (i * 11) % (0.18 * skinH),
r: 1.5 + (i % 3) * 0.6,
d: (i % 6) * 0.12,
color: "#ffc694",
}))
: [...Array(26)].map((_, i) => ({
x: 60 + (i * 23) % 500,
y: skinStart + 0.52 * skinH + (i * 13) % (0.22 * skinH),
r: 2 + (i % 3) * 0.8,
d: (i % 5) * 0.14,
color: "#ffa070",
}));
return (
);
}
/* ------------- Spectrum bar with animated marker ------------- */
function SpectrumBar({ wl }) {
const pos = ((wl - 600) / 300) * 100; // percent
const marks = [
{ nm: 600, l: "600" },
{ nm: 630, l: "630", active: wl === 630 },
{ nm: 700, l: "700" },
{ nm: 800, l: "800" },
{ nm: 850, l: "850", active: wl === 850 },
{ nm: 900, l: "900" },
];
return (
Spektrum · nm
{wl === 630 ? "Red visible" : "Near-infrared"}
{/* Color spectrum bar */}
{/* Tick marks */}
{marks.map((m, i) => {
const x = ((m.nm - 600) / 300) * 100;
return (
);
})}
{/* Animated marker */}
);
}
/* ------------- Oscilloscope waveform ------------- */
function WaveformDisplay({ wl }) {
// Shorter λ = more cycles in fixed window.
// 630nm: 9 cycles; 850nm: 5 cycles (frequency proportional to 1/λ)
const cycles = wl === 630 ? 9 : 5;
const amp = 14;
const w = 320; const h = 64;
const points = [];
for (let i = 0; i <= 160; i++) {
const x = (i / 160) * w;
const y = h/2 + Math.sin((i/160) * cycles * Math.PI * 2) * amp;
points.push(`${x},${y.toFixed(2)}`);
}
const path = "M" + points.join(" L");
const color = wl === 630 ? "#ff3848" : "#c41528";
const freqTHz = wl === 630 ? "476" : "353";
return (
{/* scan line */}
Fala · {wl === 630 ? "Widzialna" : "Podczerwień"}
{freqTHz} THz
);
}
/* ==========================================================
SECTION: How it works — 10 min session, 3 steps + live timer
========================================================== */
function HowItWorksSection() {
const { t } = useApp();
const copy = t.howItWorks;
const ref = useReveal();
const [secs, setSecs] = useState(600);
const [running, setRunning] = useState(false);
useEffect(() => {
if (!running) return;
if (secs <= 0) { setRunning(false); return; }
const id = setTimeout(() => setSecs((s) => s - 1), 1000);
return () => clearTimeout(id);
}, [running, secs]);
const mm = String(Math.floor(secs/60)).padStart(2,"0");
const ss = String(secs%60).padStart(2,"0");
const pct = 1 - secs/600;
return (
{copy.eyebrow}
{copy.title}
{copy.subtitle}
{/* Timer */}
{/* Progress ring */}
{copy.timerLabel}
{mm}:{ss}
{copy.steps.map((s, i) => (
))}
);
}
/* ==========================================================
SECTION: Products — 3 cards, hover glow
========================================================== */
function ProductsSection() {
const { t } = useApp();
const copy = t.products;
const ref = useReveal();
return (
{copy.eyebrow}
{copy.title}
{copy.subtitle}
{copy.items.map((p) =>
) }
);
}
function ProductCard({ p, cta }) {
const [hover, setHover] = useState(false);
const stripeUrl = (window.LUMAFLEX_CONFIG?.stripe?.[p.slug]) || "";
const href = stripeUrl || "#compare";
const target = stripeUrl ? "_blank" : undefined;
const rel = stripeUrl ? "noopener" : undefined;
return (
setHover(true)} onMouseLeave={() => setHover(false)}
style={{
background:"#f5f5f7",
borderRadius: 22,
padding: 32,
position:"relative",
overflow:"hidden",
transition:"all 400ms cubic-bezier(0.28,0,0.22,1)",
transform: hover ? "translateY(-4px)" : "translateY(0)",
}}>
);
}
Object.assign(window, { WavelengthsSection, HowItWorksSection, ProductsSection });