Compare commits
2 commits
13ffb8754b
...
da79e359fc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da79e359fc | ||
|
|
605bc3d025 |
14
src/App.tsx
14
src/App.tsx
|
|
@ -10,7 +10,7 @@ import CTASection from './components/CTASection';
|
||||||
import Footer from './components/Footer';
|
import Footer from './components/Footer';
|
||||||
import PrivacyPolicyPage from './components/PrivacyPolicyPage';
|
import PrivacyPolicyPage from './components/PrivacyPolicyPage';
|
||||||
import PlatformPage from './components/PlatformPage';
|
import PlatformPage from './components/PlatformPage';
|
||||||
import { useLang, stripLangPrefix } from './i18n';
|
import { useLang, useT, stripLangPrefix } from './i18n';
|
||||||
import { usePathname } from './router';
|
import { usePathname } from './router';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
@ -18,15 +18,21 @@ function App() {
|
||||||
// of leaving a stale page under a changed URL.
|
// of leaving a stale page under a changed URL.
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
const t = useT();
|
||||||
// One route table for both languages: /en/privacy and /privacy both match
|
// One route table for both languages: /en/privacy and /privacy both match
|
||||||
// '/privacy' here.
|
// '/privacy' here.
|
||||||
const route = stripLangPrefix(pathname);
|
const route = stripLangPrefix(pathname);
|
||||||
|
|
||||||
// index.html ships lang="pl"; keep the document honest on the English routes
|
// index.html ships the Polish lang/title/description; keep the document
|
||||||
// (screen readers, translation prompts, search engines all read this).
|
// honest on the English routes (screen readers, translation prompts, the
|
||||||
|
// browser tab, search engines all read these).
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.documentElement.lang = lang;
|
document.documentElement.lang = lang;
|
||||||
}, [lang]);
|
document.title = t.meta.title;
|
||||||
|
document
|
||||||
|
.querySelector('meta[name="description"]')
|
||||||
|
?.setAttribute('content', t.meta.description);
|
||||||
|
}, [lang, t]);
|
||||||
|
|
||||||
// The landing sections are client-rendered, so when a /#section URL arrives
|
// The landing sections are client-rendered, so when a /#section URL arrives
|
||||||
// with the initial request — following "Oferta"/"Kontakt" from /privacy or
|
// with the initial request — following "Oferta"/"Kontakt" from /privacy or
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { CheckCircle2 } from 'lucide-react';
|
import { CheckCircle2 } from 'lucide-react';
|
||||||
|
import { useT } from '../i18n';
|
||||||
|
|
||||||
const About = () => {
|
const About = () => {
|
||||||
const points = [
|
const t = useT();
|
||||||
"Strategia bez hype’u",
|
|
||||||
"Automatyzacje, które da się utrzymać",
|
|
||||||
"Agenci pod kontrolą człowieka"
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="about" className="py-24 relative overflow-hidden">
|
<section id="about" className="py-24 relative overflow-hidden">
|
||||||
|
|
@ -19,14 +16,11 @@ const About = () => {
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
transition={{ duration: 0.6 }}
|
transition={{ duration: 0.6 }}
|
||||||
>
|
>
|
||||||
<h2 className="text-3xl md:text-4xl font-bold mb-8">Czym jest humanAI?</h2>
|
<h2 className="text-3xl md:text-4xl font-bold mb-8">{t.about.title}</h2>
|
||||||
<p className="text-xl text-muted leading-relaxed mb-12">
|
<p className="text-xl text-muted leading-relaxed mb-12">{t.about.lead}</p>
|
||||||
humanAI łączy doradztwo, automatyzację, budowę produktów i własne środowisko agentowe.
|
|
||||||
Pomagamy przejść od ciekawości wokół AI do działających procesów, narzędzi i systemów.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="grid md:grid-cols-3 gap-6">
|
<div className="grid md:grid-cols-3 gap-6">
|
||||||
{points.map((point, index) => (
|
{t.about.points.map((point, index) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={index}
|
key={index}
|
||||||
initial={{ opacity: 0, y: 10 }}
|
initial={{ opacity: 0, y: 10 }}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,20 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { audience } from '../data/content';
|
|
||||||
import { UserCheck } from 'lucide-react';
|
import { UserCheck } from 'lucide-react';
|
||||||
|
import { useT } from '../i18n';
|
||||||
|
|
||||||
const Audience = () => {
|
const Audience = () => {
|
||||||
|
const t = useT();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="py-24 relative overflow-hidden">
|
<section className="py-24 relative overflow-hidden">
|
||||||
<div className="container mx-auto px-6">
|
<div className="container mx-auto px-6">
|
||||||
<div className="mb-16 text-center">
|
<div className="mb-16 text-center">
|
||||||
<h2 className="text-3xl md:text-5xl font-bold mb-6">Dla kogo</h2>
|
<h2 className="text-3xl md:text-5xl font-bold mb-6">{t.audience.title}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-wrap justify-center gap-6">
|
<div className="flex flex-wrap justify-center gap-6">
|
||||||
{audience.map((item, index) => (
|
{t.audience.items.map((item, index) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={index}
|
key={index}
|
||||||
initial={{ opacity: 0, scale: 0.9 }}
|
initial={{ opacity: 0, scale: 0.9 }}
|
||||||
|
|
|
||||||
|
|
@ -100,12 +100,8 @@ const CTASection = () => {
|
||||||
|
|
||||||
<div className="grid md:grid-cols-2 gap-12 relative z-10">
|
<div className="grid md:grid-cols-2 gap-12 relative z-10">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-4xl font-bold mb-6">
|
<h2 className="text-4xl font-bold mb-6">{t.cta.title}</h2>
|
||||||
Masz proces, który powinien już pracować sam?
|
<p className="text-xl text-muted mb-8">{t.cta.lead}</p>
|
||||||
</h2>
|
|
||||||
<p className="text-xl text-muted mb-8">
|
|
||||||
Opowiedz, co robisz ręcznie, powtarzalnie albo zbyt wolno. Sprawdzimy, czy AI może to uprościć.
|
|
||||||
</p>
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex items-center gap-3 text-primary">
|
<div className="flex items-center gap-3 text-primary">
|
||||||
<Mail size={20} />
|
<Mail size={20} />
|
||||||
|
|
@ -119,9 +115,7 @@ const CTASection = () => {
|
||||||
support@gethumanai.pl
|
support@gethumanai.pl
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-muted italic">
|
<p className="text-sm text-muted italic">{t.cta.quote}</p>
|
||||||
"Na start wystarczy jeden proces, jeden problem albo jeden pomysł."
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { localizePath, stripLangPrefix, useLang } from '../i18n';
|
import { localizePath, stripLangPrefix, useLang, useT } from '../i18n';
|
||||||
|
|
||||||
const Footer = () => {
|
const Footer = () => {
|
||||||
const currentYear = new Date().getFullYear();
|
const currentYear = new Date().getFullYear();
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
const t = useT();
|
||||||
|
|
||||||
// Same reasoning as the /#section nav links below: this footer is shared with
|
// Same reasoning as the /#section nav links below: this footer is shared with
|
||||||
// /privacy and /platforma, so the logo needs a real href to get home from
|
// /privacy and /platforma, so the logo needs a real href to get home from
|
||||||
|
|
@ -24,9 +25,7 @@ const Footer = () => {
|
||||||
<a href={localizePath('/', lang)} onClick={handleLogoClick} className="text-2xl font-bold tracking-tight mb-4 block">
|
<a href={localizePath('/', lang)} onClick={handleLogoClick} className="text-2xl font-bold tracking-tight mb-4 block">
|
||||||
human<span className="text-primary">AI</span>
|
human<span className="text-primary">AI</span>
|
||||||
</a>
|
</a>
|
||||||
<p className="text-muted max-w-xs mb-6">
|
<p className="text-muted max-w-xs mb-6">{t.footer.tagline}</p>
|
||||||
ludzka strona AI — budujemy praktyczne systemy AI dla ludzi i organizacji.
|
|
||||||
</p>
|
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<a href="mailto:hello@gethumanai.pl" className="text-foreground/80 hover:text-primary transition-colors">
|
<a href="mailto:hello@gethumanai.pl" className="text-foreground/80 hover:text-primary transition-colors">
|
||||||
hello@gethumanai.pl
|
hello@gethumanai.pl
|
||||||
|
|
@ -38,25 +37,25 @@ const Footer = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-bold mb-6 text-sm uppercase tracking-wider">Nawigacja</h4>
|
<h4 className="font-bold mb-6 text-sm uppercase tracking-wider">{t.footer.navHeading}</h4>
|
||||||
<ul className="space-y-4">
|
<ul className="space-y-4">
|
||||||
<li><a href={localizePath('/#offerings', lang)} className="text-muted hover:text-foreground transition-colors">Oferta</a></li>
|
<li><a href={localizePath('/#offerings', lang)} className="text-muted hover:text-foreground transition-colors">{t.nav.offerings}</a></li>
|
||||||
<li><a href={localizePath('/platforma', lang)} className="text-muted hover:text-foreground transition-colors">Platforma</a></li>
|
<li><a href={localizePath('/platforma', lang)} className="text-muted hover:text-foreground transition-colors">{t.nav.platform}</a></li>
|
||||||
<li><a href={localizePath('/#contact', lang)} className="text-muted hover:text-foreground transition-colors">Kontakt</a></li>
|
<li><a href={localizePath('/#contact', lang)} className="text-muted hover:text-foreground transition-colors">{t.nav.contact}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-bold mb-6 text-sm uppercase tracking-wider">Prywatność</h4>
|
<h4 className="font-bold mb-6 text-sm uppercase tracking-wider">{t.footer.privacyHeading}</h4>
|
||||||
<ul className="space-y-4">
|
<ul className="space-y-4">
|
||||||
<li><a href={localizePath('/privacy', lang)} className="text-muted hover:text-foreground transition-colors">Polityka prywatności</a></li>
|
<li><a href={localizePath('/privacy', lang)} className="text-muted hover:text-foreground transition-colors">{t.footer.privacyPolicy}</a></li>
|
||||||
<li><a href={localizePath('/privacy#cookies', lang)} className="text-muted hover:text-foreground transition-colors">Cookies</a></li>
|
<li><a href={localizePath('/privacy#cookies', lang)} className="text-muted hover:text-foreground transition-colors">{t.footer.cookies}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="pt-8 border-t border-white/5 flex flex-col md:flex-row justify-between items-center gap-4 text-sm text-muted">
|
<div className="pt-8 border-t border-white/5 flex flex-col md:flex-row justify-between items-center gap-4 text-sm text-muted">
|
||||||
<p>© {currentYear} humanAI. Wszelkie prawa zastrzeżone.</p>
|
<p>© {currentYear} humanAI. {t.footer.rights}</p>
|
||||||
<div className="flex gap-8">
|
<div className="flex gap-8">
|
||||||
<span className="hover:text-foreground transition-colors cursor-pointer">LinkedIn</span>
|
<span className="hover:text-foreground transition-colors cursor-pointer">LinkedIn</span>
|
||||||
<span className="hover:text-foreground transition-colors cursor-pointer">X / Twitter</span>
|
<span className="hover:text-foreground transition-colors cursor-pointer">X / Twitter</span>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Menu, X } from 'lucide-react';
|
import { Menu, X } from 'lucide-react';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { localizePath, useLang } from '../i18n';
|
import { localizePath, useLang, useT } from '../i18n';
|
||||||
import LangSwitcher from './LangSwitcher';
|
import LangSwitcher from './LangSwitcher';
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
const t = useT();
|
||||||
const [isScrolled, setIsScrolled] = useState(false);
|
const [isScrolled, setIsScrolled] = useState(false);
|
||||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||||
|
|
||||||
|
|
@ -19,13 +20,12 @@ const Header = () => {
|
||||||
|
|
||||||
// Root-relative so the links also work from the standalone pages (/privacy,
|
// Root-relative so the links also work from the standalone pages (/privacy,
|
||||||
// /platforma), where these sections do not exist — and localized so they keep
|
// /platforma), where these sections do not exist — and localized so they keep
|
||||||
// the visitor on the language they are currently reading. Labels stay Polish
|
// the visitor on the language they are currently reading.
|
||||||
// until the section copy itself is translated.
|
|
||||||
const navLinks = [
|
const navLinks = [
|
||||||
{ name: 'Oferta', href: localizePath('/#offerings', lang) },
|
{ name: t.nav.offerings, href: localizePath('/#offerings', lang) },
|
||||||
{ name: 'Platforma', href: localizePath('/platforma', lang) },
|
{ name: t.nav.platform, href: localizePath('/platforma', lang) },
|
||||||
{ name: 'Proces', href: localizePath('/#process', lang) },
|
{ name: t.nav.process, href: localizePath('/#process', lang) },
|
||||||
{ name: 'Kontakt', href: localizePath('/#contact', lang) },
|
{ name: t.nav.contact, href: localizePath('/#contact', lang) },
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -51,7 +51,7 @@ const Header = () => {
|
||||||
</a>
|
</a>
|
||||||
))}
|
))}
|
||||||
<a href={localizePath('/#contact', lang)} className="btn-primary text-sm px-5 py-2">
|
<a href={localizePath('/#contact', lang)} className="btn-primary text-sm px-5 py-2">
|
||||||
Porozmawiajmy
|
{t.nav.cta}
|
||||||
</a>
|
</a>
|
||||||
<LangSwitcher />
|
<LangSwitcher />
|
||||||
</nav>
|
</nav>
|
||||||
|
|
@ -90,7 +90,7 @@ const Header = () => {
|
||||||
className="btn-primary text-center"
|
className="btn-primary text-center"
|
||||||
onClick={() => setIsMobileMenuOpen(false)}
|
onClick={() => setIsMobileMenuOpen(false)}
|
||||||
>
|
>
|
||||||
Porozmawiajmy
|
{t.nav.cta}
|
||||||
</a>
|
</a>
|
||||||
<div className="pt-2 border-t border-white/10">
|
<div className="pt-2 border-t border-white/10">
|
||||||
<LangSwitcher onSwitch={() => setIsMobileMenuOpen(false)} />
|
<LangSwitcher onSwitch={() => setIsMobileMenuOpen(false)} />
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { ArrowRight } from 'lucide-react';
|
import { ArrowRight } from 'lucide-react';
|
||||||
|
import { useT } from '../i18n';
|
||||||
|
|
||||||
const Hero = () => {
|
const Hero = () => {
|
||||||
|
const t = useT();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="relative min-h-screen flex items-center pt-20 overflow-hidden">
|
<section className="relative min-h-screen flex items-center pt-20 overflow-hidden">
|
||||||
{/* Background elements */}
|
{/* Background elements */}
|
||||||
|
|
@ -17,18 +20,20 @@ const Hero = () => {
|
||||||
transition={{ duration: 0.6 }}
|
transition={{ duration: 0.6 }}
|
||||||
>
|
>
|
||||||
<h1 className="text-5xl md:text-7xl font-extrabold leading-tight mb-6">
|
<h1 className="text-5xl md:text-7xl font-extrabold leading-tight mb-6">
|
||||||
Budujemy <span className="text-gradient">ludzką stronę</span> AI
|
{t.hero.titleBefore}
|
||||||
|
<span className="text-gradient">{t.hero.titleAccent}</span>
|
||||||
|
{t.hero.titleAfter}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-lg md:text-xl text-muted mb-10 max-w-lg leading-relaxed">
|
<p className="text-lg md:text-xl text-muted mb-10 max-w-lg leading-relaxed">
|
||||||
Projektujemy i wdrażamy systemy AI, które pomagają ludziom pracować szybciej, mądrzej i spokojniej — od automatyzacji po multiagentowe platformy produktowe.
|
{t.hero.subtitle}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-col sm:flex-row gap-4">
|
<div className="flex flex-col sm:flex-row gap-4">
|
||||||
<a href="#contact" className="btn-primary">
|
<a href="#contact" className="btn-primary">
|
||||||
Zacznijmy od audytu AI
|
{t.hero.ctaPrimary}
|
||||||
<ArrowRight className="ml-2" size={20} />
|
<ArrowRight className="ml-2" size={20} />
|
||||||
</a>
|
</a>
|
||||||
<a href="#offerings" className="btn-secondary">
|
<a href="#offerings" className="btn-secondary">
|
||||||
Zobacz ofertę
|
{t.hero.ctaSecondary}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,29 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { offerings } from '../data/content';
|
import { offerings } from '../data/content';
|
||||||
|
import type { OfferingId } from '../data/content';
|
||||||
|
import { localizePath, useLang, useT } from '../i18n';
|
||||||
import { ArrowRight, ChevronRight } from 'lucide-react';
|
import { ArrowRight, ChevronRight } from 'lucide-react';
|
||||||
|
import type { LucideIcon } from 'lucide-react';
|
||||||
|
|
||||||
const OfferingCard = ({ offering, index }: { offering: any, index: number }) => {
|
type Copy = {
|
||||||
const Icon = offering.icon;
|
title: string;
|
||||||
|
description: string;
|
||||||
|
points: readonly string[];
|
||||||
|
linkLabel?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const OfferingCard = ({
|
||||||
|
copy,
|
||||||
|
icon: Icon,
|
||||||
|
href,
|
||||||
|
index,
|
||||||
|
}: {
|
||||||
|
copy: Copy;
|
||||||
|
icon: LucideIcon;
|
||||||
|
href?: string;
|
||||||
|
index: number;
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 30 }}
|
initial={{ opacity: 0, y: 30 }}
|
||||||
|
|
@ -17,24 +35,24 @@ const OfferingCard = ({ offering, index }: { offering: any, index: number }) =>
|
||||||
<div className="w-14 h-14 rounded-xl bg-primary/10 flex items-center justify-center text-primary mb-6 group-hover:scale-110 transition-transform duration-500">
|
<div className="w-14 h-14 rounded-xl bg-primary/10 flex items-center justify-center text-primary mb-6 group-hover:scale-110 transition-transform duration-500">
|
||||||
<Icon size={28} />
|
<Icon size={28} />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-2xl font-bold mb-4">{offering.title}</h3>
|
<h3 className="text-2xl font-bold mb-4">{copy.title}</h3>
|
||||||
<p className="text-muted mb-8 leading-relaxed">
|
<p className="text-muted mb-8 leading-relaxed">
|
||||||
{offering.description}
|
{copy.description}
|
||||||
</p>
|
</p>
|
||||||
<ul className="space-y-3">
|
<ul className="space-y-3">
|
||||||
{offering.points.map((point: string, i: number) => (
|
{copy.points.map((point, i) => (
|
||||||
<li key={i} className="flex items-center text-sm text-foreground/80">
|
<li key={i} className="flex items-center text-sm text-foreground/80">
|
||||||
<ChevronRight className="text-primary mr-2" size={16} />
|
<ChevronRight className="text-primary mr-2" size={16} />
|
||||||
{point}
|
{point}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
{offering.link && (
|
{href && copy.linkLabel && (
|
||||||
<a
|
<a
|
||||||
href={offering.link.href}
|
href={href}
|
||||||
className="mt-8 inline-flex items-center text-sm font-semibold text-primary hover:underline"
|
className="mt-8 inline-flex items-center text-sm font-semibold text-primary hover:underline"
|
||||||
>
|
>
|
||||||
{offering.link.label}
|
{copy.linkLabel}
|
||||||
<ArrowRight className="ml-2" size={16} />
|
<ArrowRight className="ml-2" size={16} />
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
|
|
@ -43,19 +61,29 @@ const OfferingCard = ({ offering, index }: { offering: any, index: number }) =>
|
||||||
};
|
};
|
||||||
|
|
||||||
const Offerings = () => {
|
const Offerings = () => {
|
||||||
|
const t = useT();
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="offerings" className="py-24 relative bg-surface/30">
|
<section id="offerings" className="py-24 relative bg-surface/30">
|
||||||
<div className="container mx-auto px-6">
|
<div className="container mx-auto px-6">
|
||||||
<div className="mb-16">
|
<div className="mb-16">
|
||||||
<h2 className="text-3xl md:text-5xl font-bold mb-6">Nasza Oferta</h2>
|
<h2 className="text-3xl md:text-5xl font-bold mb-6">{t.offerings.title}</h2>
|
||||||
<p className="text-xl text-muted max-w-2xl">
|
<p className="text-xl text-muted max-w-2xl">
|
||||||
Budujemy kompleksowe rozwiązania, które przekładają potencjał sztucznej inteligencji na realne wyniki biznesowe.
|
{t.offerings.lead}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid md:grid-cols-2 gap-8">
|
<div className="grid md:grid-cols-2 gap-8">
|
||||||
{offerings.map((offering, index) => (
|
{offerings.map((offering, index) => (
|
||||||
<OfferingCard key={offering.id} offering={offering} index={index} />
|
<OfferingCard
|
||||||
|
key={offering.id}
|
||||||
|
icon={offering.icon}
|
||||||
|
// Structure lives in content.ts, copy in i18n — joined by id.
|
||||||
|
copy={t.offerings.items[offering.id as OfferingId]}
|
||||||
|
href={'link' in offering ? localizePath(offering.link.href, lang) : undefined}
|
||||||
|
index={index}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,28 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ArrowRight, Building2, CheckCircle2, ChevronRight, Home } from 'lucide-react';
|
import { ArrowRight, Building2, CheckCircle2, ChevronRight, Home } from 'lucide-react';
|
||||||
import Footer from './Footer';
|
import Footer from './Footer';
|
||||||
import { localizePath, useLang } from '../i18n';
|
import RichText from './RichText';
|
||||||
|
import { localizePath, useLang, useT } from '../i18n';
|
||||||
|
import type { Bullet } from '../i18n/types';
|
||||||
import LangSwitcher from './LangSwitcher';
|
import LangSwitcher from './LangSwitcher';
|
||||||
|
|
||||||
// Standalone product page at /platforma (and /en/platforma). The copy is not
|
// Standalone product page at /platforma (and /en/platforma). All copy lives in
|
||||||
// localized yet — the site only translates contact.* and privacy.* — but the
|
// i18n under `platform`; paragraphs that carry inline emphasis are stored as
|
||||||
// links are, so reaching this page from /en does not strand you in Polish.
|
// runs and rendered through <RichText> (see i18n/types.ts).
|
||||||
const PlatformPage = () => {
|
const PlatformPage = () => {
|
||||||
|
const t = useT();
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
const renderBullet = (bullet: Bullet, i: number, Icon: typeof ChevronRight) => (
|
||||||
|
<li key={i} className="flex gap-3">
|
||||||
|
<Icon className="text-primary shrink-0 mt-1" size={18} />
|
||||||
|
<span className="text-foreground/80 leading-relaxed">
|
||||||
|
{bullet.lead && <strong className="text-foreground font-semibold">{bullet.lead}</strong>}
|
||||||
|
{bullet.body && <RichText content={bullet.body} />}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background selection:bg-primary selection:text-background">
|
<div className="min-h-screen bg-background selection:bg-primary selection:text-background">
|
||||||
<header className="bg-background/80 backdrop-blur-lg border-b border-white/5">
|
<header className="bg-background/80 backdrop-blur-lg border-b border-white/5">
|
||||||
|
|
@ -19,7 +32,7 @@ const PlatformPage = () => {
|
||||||
</a>
|
</a>
|
||||||
<div className="flex items-center gap-6">
|
<div className="flex items-center gap-6">
|
||||||
<a href={localizePath('/', lang)} className="text-sm text-muted hover:text-foreground transition-colors">
|
<a href={localizePath('/', lang)} className="text-sm text-muted hover:text-foreground transition-colors">
|
||||||
← Wróć na stronę główną
|
{t.common.backToHome}
|
||||||
</a>
|
</a>
|
||||||
<LangSwitcher />
|
<LangSwitcher />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -27,83 +40,42 @@ const PlatformPage = () => {
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main className="container mx-auto px-6 py-16 max-w-3xl">
|
<main className="container mx-auto px-6 py-16 max-w-3xl">
|
||||||
<h1 className="text-4xl md:text-5xl font-bold mb-3">Nadzór nad systemami agentowymi</h1>
|
<h1 className="text-4xl md:text-5xl font-bold mb-3">{t.platform.title}</h1>
|
||||||
<p className="text-muted text-sm italic mb-16">
|
<p className="text-muted text-sm italic mb-16">{t.platform.workingName}</p>
|
||||||
(nazwa robocza — do potwierdzenia przed publikacją)
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="space-y-16">
|
<div className="space-y-16">
|
||||||
{/* ── Kto pilnuje agentów ───────────────────────────────────────── */}
|
{/* ── Kto pilnuje agentów ───────────────────────────────────────── */}
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl md:text-3xl font-bold mb-6">
|
<h2 className="text-2xl md:text-3xl font-bold mb-6">{t.platform.watchers.title}</h2>
|
||||||
Kto pilnuje agentów, którzy pilnują wszystkiego innego?
|
{t.platform.watchers.paragraphs.map((paragraph, i) => (
|
||||||
</h2>
|
<p key={i} className="text-foreground/80 leading-relaxed mb-4 last:mb-0">
|
||||||
<p className="text-foreground/80 leading-relaxed mb-4">
|
<RichText content={paragraph} />
|
||||||
{'Automatyzacje i agenci AI potrafią pracować bez człowieka — porządkować dokumenty, sterować urządzeniami, reagować na zdarzenia. Ale zostawione bez nadzoru, prędzej czy później zrobią coś, czego nikt nie chciał. Zbudowaliśmy system, który '}
|
</p>
|
||||||
<strong className="text-foreground font-semibold">
|
))}
|
||||||
obserwuje inne systemy — w tym inne systemy agentowe
|
|
||||||
</strong>
|
|
||||||
{' — wykrywa, że coś nie działa, diagnozuje przyczynę i naprawia problem sam. A tam, gdzie decyzja jest nieodwracalna, czeka na człowieka.'}
|
|
||||||
</p>
|
|
||||||
<p className="text-foreground/80 leading-relaxed">
|
|
||||||
{'To nie koncept. Ten sam rdzeń od miesięcy prowadzi naszą własną, rozproszoną infrastrukturę — i przeszedł przez realne awarie, z których każda coś go nauczyła.'}
|
|
||||||
</p>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── Problem, który znika ──────────────────────────────────────── */}
|
{/* ── Problem, który znika ──────────────────────────────────────── */}
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl md:text-3xl font-bold mb-6">Problem, który znika</h2>
|
<h2 className="text-2xl md:text-3xl font-bold mb-6">{t.platform.problem.title}</h2>
|
||||||
<ul className="space-y-4">
|
<ul className="space-y-4">
|
||||||
<li className="flex gap-3">
|
{t.platform.problem.bullets.map((bullet, i) => renderBullet(bullet, i, ChevronRight))}
|
||||||
<ChevronRight className="text-primary shrink-0 mt-1" size={18} />
|
|
||||||
<span className="text-foreground/80 leading-relaxed">
|
|
||||||
<strong className="text-foreground font-semibold">Autonomia bez ram to ryzyko.</strong>
|
|
||||||
{' Coś, co może samo otworzyć bramę albo zmienić konfigurację usługi, potrzebuje jasnej granicy: co wolno zrobić samodzielnie, co wymaga zgody człowieka.'}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className="flex gap-3">
|
|
||||||
<ChevronRight className="text-primary shrink-0 mt-1" size={18} />
|
|
||||||
<span className="text-foreground/80 leading-relaxed">
|
|
||||||
<strong className="text-foreground font-semibold">Automatyka to nie inteligencja.</strong>
|
|
||||||
{' Wiele domów i firm ma już systemy, które '}
|
|
||||||
<em>wykonują</em>
|
|
||||||
{' polecenia — światła, rolety, bramy, obieg dokumentów. Problem w tym, że nic tego nie '}
|
|
||||||
<em>rozumie</em>
|
|
||||||
{': nie kojarzy faktów, nie reaguje na kontekst, nie uczy się.'}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className="flex gap-3">
|
|
||||||
<ChevronRight className="text-primary shrink-0 mt-1" size={18} />
|
|
||||||
<span className="text-foreground/80 leading-relaxed">
|
|
||||||
<strong className="text-foreground font-semibold">Wiedza znika.</strong>
|
|
||||||
{' Dlaczego coś się zepsuło miesiąc temu i co wtedy pomogło? Zwykle ta wiedza siedzi w głowie jednej osoby albo w nieprzeszukiwalnym stosie dokumentów.'}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── Jak to działa ─────────────────────────────────────────────── */}
|
{/* ── Jak to działa ─────────────────────────────────────────────── */}
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl md:text-3xl font-bold mb-6">Jak to działa</h2>
|
<h2 className="text-2xl md:text-3xl font-bold mb-6">{t.platform.howItWorks.title}</h2>
|
||||||
|
|
||||||
<p className="text-foreground/80 leading-relaxed mb-4">
|
<p className="text-foreground/80 leading-relaxed mb-4">
|
||||||
<strong className="text-foreground font-semibold">
|
<RichText content={t.platform.howItWorks.core} />
|
||||||
Rdzeń nadzoru — jeden, wspólny dla wszystkiego.
|
|
||||||
</strong>
|
|
||||||
{' Lekki agent na każdej maszynie zgłasza stan usług. Centralny „mózg" buduje z tego pełny obraz sytuacji, wykrywa odchylenia od tego, co powinno działać, i albo naprawia problem sam — jeśli akcja jest bezpieczna — albo pyta człowieka, jeśli nie jest.'}
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="text-foreground/80 leading-relaxed mb-10">
|
<p className="text-foreground/80 leading-relaxed mb-10">
|
||||||
{'To „pytanie człowieka" nie jest teorią. Przykład z życia: system zauważa, że wyjechałeś na weekend, a rolety zostały otwarte. Sam ich nie zamknie — wysyła Ci wiadomość: '}
|
<RichText content={t.platform.howItWorks.humanAsk} />
|
||||||
<em className="text-foreground">{'„Nie zamknąłeś rolet. Zamknąć teraz?"'}</em>
|
|
||||||
{' Odpisujesz „tak" — i dopiero wtedy system zleca to istniejącej automatyce domowej. Decyzja zawsze należy do Ciebie; wykonanie bierze na siebie system.'}
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="text-foreground/80 leading-relaxed mb-8">
|
<p className="text-foreground/80 leading-relaxed mb-8">
|
||||||
<strong className="text-foreground font-semibold">
|
<RichText content={t.platform.howItWorks.tailored} />
|
||||||
System roboczy — budowany pod Ciebie.
|
|
||||||
</strong>
|
|
||||||
{' To, co ten nadzór pilnuje, dopasowujemy do konkretnej potrzeby.'}
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Dwie ścieżki, jeden rdzeń — karty obok siebie */}
|
{/* Dwie ścieżki, jeden rdzeń — karty obok siebie */}
|
||||||
|
|
@ -112,11 +84,9 @@ const PlatformPage = () => {
|
||||||
<div className="w-12 h-12 rounded-xl bg-primary/10 flex items-center justify-center text-primary mb-6">
|
<div className="w-12 h-12 rounded-xl bg-primary/10 flex items-center justify-center text-primary mb-6">
|
||||||
<Home size={24} />
|
<Home size={24} />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-xl font-bold mb-4">Dla domu</h3>
|
<h3 className="text-xl font-bold mb-4">{t.platform.howItWorks.home.title}</h3>
|
||||||
<p className="text-foreground/80 leading-relaxed">
|
<p className="text-foreground/80 leading-relaxed">
|
||||||
{'Masz już zautomatyzowany dom — światła, rolety, bramy, klimatyzację — ale to automatyka, nie inteligencja: robi dokładnie to, co jej każesz, i nic więcej. Dokładamy warstwę, która '}
|
<RichText content={t.platform.howItWorks.home.body} />
|
||||||
<strong className="text-foreground font-semibold">rozumie kontekst</strong>
|
|
||||||
{': rozpoznaje Twój samochód po tablicy rejestracyjnej i otwiera bramę, zanim dojedziesz do niej. Widzi na kamerach, że nikogo nie ma w domu, i sama wygasza światła na noc. Zauważa, że wyjechałeś, a coś zostało otwarte — i pyta, zanim cokolwiek zrobi.'}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -124,116 +94,52 @@ const PlatformPage = () => {
|
||||||
<div className="w-12 h-12 rounded-xl bg-secondary/10 flex items-center justify-center text-secondary mb-6">
|
<div className="w-12 h-12 rounded-xl bg-secondary/10 flex items-center justify-center text-secondary mb-6">
|
||||||
<Building2 size={24} />
|
<Building2 size={24} />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-xl font-bold mb-4">Dla firm</h3>
|
<h3 className="text-xl font-bold mb-4">{t.platform.howItWorks.business.title}</h3>
|
||||||
<p className="text-foreground/80 leading-relaxed">
|
<p className="text-foreground/80 leading-relaxed">
|
||||||
{'Masz bałagan w dokumentach, obieg papierowy, który nikogo nie satysfakcjonuje, i dane rozsiane po cudzych chmurach, nad którymi nie masz pełnej kontroli. Budujemy Ci paperless na Twoich własnych serwerach — z własną domeną i autoryzacją, bez uzależnienia od zewnętrznych dostawców chmury. Indeksujemy dokumenty, zdjęcia i korespondencję tak, żeby dało się to przeszukać po sensie, nie po słowach kluczowych. Interfejs — webowy panel, a jeśli to naprawdę pasuje do Twojej firmy, także chatbot — dobieramy do tego, czego faktycznie potrzebujesz, a nie do tego, co akurat modne.'}
|
<RichText content={t.platform.howItWorks.business.body} />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-foreground/80 leading-relaxed mt-8">
|
<p className="text-foreground/80 leading-relaxed mt-8">
|
||||||
{'Obie ścieżki łączy to samo: cała konfiguracja tego, co ma działać, jest zapisana jako kod w repozytorium — każda zmiana ma historię i da się ją cofnąć — i obie działają pod tym samym nadzorem, który pilnuje, żeby nic nie zawiodło po cichu.'}
|
<RichText content={t.platform.howItWorks.shared} />
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── Pod maską ─────────────────────────────────────────────────── */}
|
{/* ── Pod maską ─────────────────────────────────────────────────── */}
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl md:text-3xl font-bold mb-6">Pod maską — dla ciekawych</h2>
|
<h2 className="text-2xl md:text-3xl font-bold mb-6">{t.platform.underTheHood.title}</h2>
|
||||||
<ul className="space-y-4">
|
<ul className="space-y-4">
|
||||||
<li className="flex gap-3">
|
{t.platform.underTheHood.bullets.map((bullet, i) => renderBullet(bullet, i, ChevronRight))}
|
||||||
<ChevronRight className="text-primary shrink-0 mt-1" size={18} />
|
|
||||||
<span className="text-foreground/80 leading-relaxed">
|
|
||||||
{'Baza wiedzy nie kończy jako martwy indeks. Nad wyszukiwaniem semantycznym powstaje '}
|
|
||||||
<strong className="text-foreground font-semibold">
|
|
||||||
czytelna, kompilowana wiki utrzymywana przez model językowy
|
|
||||||
</strong>
|
|
||||||
{' — pliki tekstowe z linkami między pojęciami, które agent czyta i aktualizuje tak jak człowiek utrzymuje kod. Budujemy to na otwartym standardzie (Google OKF), nie na własnym, zamkniętym formacie — żeby wiedza, którą zbudujemy, zawsze zostawała czytelna i przenośna.'}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className="flex gap-3">
|
|
||||||
<ChevronRight className="text-primary shrink-0 mt-1" size={18} />
|
|
||||||
<span className="text-foreground/80 leading-relaxed">
|
|
||||||
{'Do zapytań najpierw sięga tańszy model — droższy dopiero wtedy, gdy sprawa naprawdę tego wymaga. W praktyce: streszczenie całego korpusu dokumentów potrafiło kosztować grosze zamiast dni obliczeń.'}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className="flex gap-3">
|
|
||||||
<ChevronRight className="text-primary shrink-0 mt-1" size={18} />
|
|
||||||
<span className="text-foreground/80 leading-relaxed">
|
|
||||||
{'Nie wszystko musi być online 24 godziny na dobę. Kiedy główna maszyna śpi, zapytania automatycznie przejmuje zapasowy sprzęt — bez przerwy zauważalnej dla użytkownika.'}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className="flex gap-3">
|
|
||||||
<ChevronRight className="text-primary shrink-0 mt-1" size={18} />
|
|
||||||
<span className="text-foreground/80 leading-relaxed">
|
|
||||||
{'System sam odróżnia „usługa nie działa" od „usługa śpi zgodnie z planem" — co wcześniej było częstym źródłem fałszywych alarmów.'}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── Dlaczego to ───────────────────────────────────────────────── */}
|
{/* ── Dlaczego to ───────────────────────────────────────────────── */}
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl md:text-3xl font-bold mb-6">
|
<h2 className="text-2xl md:text-3xl font-bold mb-6">{t.platform.why.title}</h2>
|
||||||
Dlaczego to, a nie zwykły monitoring czy zwykła automatyka
|
|
||||||
</h2>
|
|
||||||
<ul className="space-y-4">
|
<ul className="space-y-4">
|
||||||
<li className="flex gap-3">
|
{t.platform.why.bullets.map((bullet, i) => renderBullet(bullet, i, CheckCircle2))}
|
||||||
<CheckCircle2 className="text-primary shrink-0 mt-1" size={18} />
|
|
||||||
<span className="text-foreground/80 leading-relaxed">
|
|
||||||
<strong className="text-foreground font-semibold">
|
|
||||||
Rozumiemy kontekst, nie tylko wykonujemy polecenia.
|
|
||||||
</strong>
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className="flex gap-3">
|
|
||||||
<CheckCircle2 className="text-primary shrink-0 mt-1" size={18} />
|
|
||||||
<span className="text-foreground/80 leading-relaxed">
|
|
||||||
<strong className="text-foreground font-semibold">Naprawiamy, nie tylko alarmujemy.</strong>
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className="flex gap-3">
|
|
||||||
<CheckCircle2 className="text-primary shrink-0 mt-1" size={18} />
|
|
||||||
<span className="text-foreground/80 leading-relaxed">
|
|
||||||
<strong className="text-foreground font-semibold">Człowiek naprawdę jest w pętli</strong>
|
|
||||||
{' — decyzje zawsze do Ciebie, wykonanie bierze na siebie system.'}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className="flex gap-3">
|
|
||||||
<CheckCircle2 className="text-primary shrink-0 mt-1" size={18} />
|
|
||||||
<span className="text-foreground/80 leading-relaxed">
|
|
||||||
<strong className="text-foreground font-semibold">Zero czarnej skrzynki.</strong>
|
|
||||||
{' Każda decyzja zostawia ślad, który można później przejrzeć.'}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className="flex gap-3">
|
|
||||||
<CheckCircle2 className="text-primary shrink-0 mt-1" size={18} />
|
|
||||||
<span className="text-foreground/80 leading-relaxed">
|
|
||||||
<strong className="text-foreground font-semibold">Twoje dane zostają Twoje</strong>
|
|
||||||
{' — własny serwer, własna domena, bez uzależnienia od cudzej chmury.'}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── Gdzie jesteśmy ────────────────────────────────────────────── */}
|
{/* ── Gdzie jesteśmy ────────────────────────────────────────────── */}
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl md:text-3xl font-bold mb-6">Uczciwie o tym, gdzie jesteśmy</h2>
|
<h2 className="text-2xl md:text-3xl font-bold mb-6">{t.platform.status.title}</h2>
|
||||||
<p className="text-foreground/80 leading-relaxed mb-4">
|
{t.platform.status.paragraphs.map((paragraph, i) => (
|
||||||
{'Rdzeń nadzoru prowadzi naszą infrastrukturę na co dzień — samonaprawa działa od początku do końca, alerty trafiają na telefon, baza wiedzy odpowiada na pytania nawet gdy główny sprzęt śpi.'}
|
<p key={i} className="text-foreground/80 leading-relaxed mb-4 last:mb-0">
|
||||||
</p>
|
<RichText content={paragraph} />
|
||||||
<p className="text-foreground/80 leading-relaxed">
|
</p>
|
||||||
{'Dedykowane wdrożenia — pod konkretny dom czy firmę — budujemy w miarę potrzeb, opierając się na tym samym, sprawdzonym rdzeniu. Wolimy powiedzieć wprost, co jeszcze dopracowujemy, niż obiecywać więcej niż działa.'}
|
))}
|
||||||
</p>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── CTA ───────────────────────────────────────────────────────── */}
|
{/* ── CTA ───────────────────────────────────────────────────────── */}
|
||||||
<section className="glass-card p-10 text-center">
|
<section className="glass-card p-10 text-center">
|
||||||
<h2 className="text-2xl md:text-3xl font-bold mb-6">Umów rozmowę</h2>
|
<h2 className="text-2xl md:text-3xl font-bold mb-6">{t.platform.cta.title}</h2>
|
||||||
<p className="text-foreground/80 leading-relaxed mb-8">
|
<p className="text-foreground/80 leading-relaxed mb-8">
|
||||||
{'Powiedz, co chcesz zautomatyzować albo uporządkować — zobaczymy, jak to zbudować i objąć nadzorem, który sami na sobie testujemy.'}
|
<RichText content={t.platform.cta.body} />
|
||||||
</p>
|
</p>
|
||||||
<a href={localizePath('/#contact', lang)} className="btn-primary">
|
<a href={localizePath('/#contact', lang)} className="btn-primary">
|
||||||
Porozmawiajmy
|
{t.platform.cta.button}
|
||||||
<ArrowRight className="ml-2" size={20} />
|
<ArrowRight className="ml-2" size={20} />
|
||||||
</a>
|
</a>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ const PrivacyPolicyPage = () => {
|
||||||
</a>
|
</a>
|
||||||
<div className="flex items-center gap-6">
|
<div className="flex items-center gap-6">
|
||||||
<a href={home} className="text-sm text-muted hover:text-foreground transition-colors">
|
<a href={home} className="text-sm text-muted hover:text-foreground transition-colors">
|
||||||
{t.privacy.backToHome}
|
{t.common.backToHome}
|
||||||
</a>
|
</a>
|
||||||
<LangSwitcher />
|
<LangSwitcher />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { processSteps } from '../data/content';
|
import { processSteps } from '../data/content';
|
||||||
|
import { useT } from '../i18n';
|
||||||
|
|
||||||
const Process = () => {
|
const Process = () => {
|
||||||
|
const t = useT();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="process" className="py-24 relative bg-surface/50">
|
<section id="process" className="py-24 relative bg-surface/50">
|
||||||
<div className="container mx-auto px-6">
|
<div className="container mx-auto px-6">
|
||||||
<div className="text-center mb-16">
|
<div className="text-center mb-16">
|
||||||
<h2 className="text-3xl md:text-5xl font-bold mb-6">Od pomysłu do działającego systemu</h2>
|
<h2 className="text-3xl md:text-5xl font-bold mb-6">{t.process.title}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
|
|
@ -30,8 +33,10 @@ const Process = () => {
|
||||||
<Icon size={28} />
|
<Icon size={28} />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<span className="text-xs font-bold text-primary uppercase tracking-wider mb-2 block">Krok {step.id}</span>
|
<span className="text-xs font-bold text-primary uppercase tracking-wider mb-2 block">
|
||||||
<h3 className="text-xl font-bold">{step.title}</h3>
|
{t.process.stepLabel} {step.number}
|
||||||
|
</span>
|
||||||
|
<h3 className="text-xl font-bold">{t.process.steps[step.id]}</h3>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
36
src/components/RichText.tsx
Normal file
36
src/components/RichText.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import React from 'react';
|
||||||
|
import type { Rich } from '../i18n/types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders copy that carries inline emphasis (see i18n/types.ts). Keeps the
|
||||||
|
* dictionaries free of markup: no dangerouslySetInnerHTML, so a translation can
|
||||||
|
* never inject elements.
|
||||||
|
*/
|
||||||
|
const RichText = ({ content }: { content: Rich }) => {
|
||||||
|
if (typeof content === 'string') return <>{content}</>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{content.map((run, i) => {
|
||||||
|
if (run.em === 'strong') {
|
||||||
|
return (
|
||||||
|
<strong key={i} className="text-foreground font-semibold">
|
||||||
|
{run.t}
|
||||||
|
</strong>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (run.em === 'quote') {
|
||||||
|
return (
|
||||||
|
<em key={i} className="text-foreground">
|
||||||
|
{run.t}
|
||||||
|
</em>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (run.em === 'em') return <em key={i}>{run.t}</em>;
|
||||||
|
return <React.Fragment key={i}>{run.t}</React.Fragment>;
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RichText;
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { whyHumanAI } from '../data/content';
|
import { whyHumanAI } from '../data/content';
|
||||||
|
import { useT } from '../i18n';
|
||||||
|
|
||||||
const WhyHumanAI = () => {
|
const WhyHumanAI = () => {
|
||||||
|
const t = useT();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="py-24 relative overflow-hidden">
|
<section className="py-24 relative overflow-hidden">
|
||||||
<div className="container mx-auto px-6">
|
<div className="container mx-auto px-6">
|
||||||
|
|
@ -13,7 +16,7 @@ const WhyHumanAI = () => {
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
className="text-3xl md:text-5xl font-bold mb-8"
|
className="text-3xl md:text-5xl font-bold mb-8"
|
||||||
>
|
>
|
||||||
AI nie powinno być czarną skrzynką
|
{t.why.title}
|
||||||
</motion.h2>
|
</motion.h2>
|
||||||
<motion.p
|
<motion.p
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
|
@ -22,8 +25,7 @@ const WhyHumanAI = () => {
|
||||||
transition={{ delay: 0.1 }}
|
transition={{ delay: 0.1 }}
|
||||||
className="text-xl text-muted leading-relaxed"
|
className="text-xl text-muted leading-relaxed"
|
||||||
>
|
>
|
||||||
Najlepsze wdrożenia AI są zrozumiałe, mierzalne i bezpieczne. Dlatego projektujemy systemy,
|
{t.why.lead}
|
||||||
które pokazują swoje działanie, zostawiają ślad decyzyjny i pozwalają człowiekowi zatwierdzać ważne kroki.
|
|
||||||
</motion.p>
|
</motion.p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -32,7 +34,7 @@ const WhyHumanAI = () => {
|
||||||
const Icon = item.icon;
|
const Icon = item.icon;
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={index}
|
key={item.id}
|
||||||
initial={{ opacity: 0, scale: 0.9 }}
|
initial={{ opacity: 0, scale: 0.9 }}
|
||||||
whileInView={{ opacity: 1, scale: 1 }}
|
whileInView={{ opacity: 1, scale: 1 }}
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
|
|
@ -42,7 +44,7 @@ const WhyHumanAI = () => {
|
||||||
<div className="w-12 h-12 rounded-full bg-secondary/10 flex items-center justify-center text-secondary mb-6 group-hover:bg-secondary/20 transition-colors">
|
<div className="w-12 h-12 rounded-full bg-secondary/10 flex items-center justify-center text-secondary mb-6 group-hover:bg-secondary/20 transition-colors">
|
||||||
<Icon size={24} />
|
<Icon size={24} />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="font-semibold text-lg">{item.title}</h3>
|
<h3 className="font-semibold text-lg">{t.why.items[item.id]}</h3>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
||||||
|
|
@ -8,76 +8,39 @@ import {
|
||||||
ShieldCheck,
|
ShieldCheck,
|
||||||
BarChart3,
|
BarChart3,
|
||||||
Layout,
|
Layout,
|
||||||
MessageSquare,
|
|
||||||
Eye,
|
Eye,
|
||||||
Settings,
|
Settings,
|
||||||
Rocket
|
Rocket,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
|
// Structure only — no copy. Icons are React components and cannot live in the
|
||||||
|
// dictionaries without pulling React into them, and ids/hrefs are not
|
||||||
|
// translatable anyway. The matching text lives in i18n under the same id, so
|
||||||
|
// order and cardinality are defined once here rather than duplicated per
|
||||||
|
// language.
|
||||||
|
|
||||||
export const offerings = [
|
export const offerings = [
|
||||||
{
|
{ id: 'strategy', icon: Search },
|
||||||
id: 'strategy',
|
{ id: 'automation', icon: Workflow },
|
||||||
title: 'AI Strategy & Advisory',
|
{ id: 'products', icon: Layout },
|
||||||
description: 'Pomagamy znaleźć realne zastosowania AI w firmie, wybrać priorytety i zaplanować wdrożenia bez przepalania czasu na przypadkowe eksperymenty.',
|
// Deep-link to the dedicated product page; the label is translated.
|
||||||
points: ['audyt procesów', 'mapa use case’ów', 'priorytetyzacja wdrożeń', 'polityki bezpieczeństwa AI'],
|
{ id: 'multiagent', icon: Cpu, link: { href: '/platforma' } },
|
||||||
icon: Search
|
] as const;
|
||||||
},
|
export type OfferingId = (typeof offerings)[number]['id'];
|
||||||
{
|
|
||||||
id: 'automation',
|
|
||||||
title: 'AI Automation & Workflows',
|
|
||||||
description: 'Budujemy automatyzacje, które łączą modele AI z codziennymi narzędziami: dokumentami, mailem, CRM, bazami danych, API i wewnętrznymi procesami.',
|
|
||||||
points: ['automatyzacje operacyjne', 'integracje API', 'workflow z człowiekiem w pętli', 'monitoring i logi'],
|
|
||||||
icon: Workflow
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'products',
|
|
||||||
title: 'AI Products & Interfaces',
|
|
||||||
description: 'Projektujemy i tworzymy aplikacje, panele, chatboty, copilots i wyszukiwarki semantyczne, które przekładają AI na użyteczne doświadczenie użytkownika.',
|
|
||||||
points: ['aplikacje webowe', 'panele AI', 'chatboty i copilots', 'RAG / wyszukiwanie w wiedzy'],
|
|
||||||
icon: Layout
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'multiagent',
|
|
||||||
title: 'Multiagent Systems & Infrastructure',
|
|
||||||
description: 'Tworzymy systemy wieloagentowe, które potrafią planować, wykonywać zadania, korzystać z narzędzi i współpracować z człowiekiem przy zachowaniu kontroli i audytowalności.',
|
|
||||||
points: ['agent orchestration', 'tool registry / MCP', 'approval gates', 'pamięć, logi i obserwowalność'],
|
|
||||||
icon: Cpu,
|
|
||||||
// Deep-link to the dedicated product page (/platforma).
|
|
||||||
link: { label: 'Zobacz jak to działa', href: '/platforma' }
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export const whyHumanAI = [
|
export const whyHumanAI = [
|
||||||
{
|
{ id: 'hitl', icon: Users },
|
||||||
title: 'Human-in-the-loop',
|
{ id: 'security', icon: ShieldCheck },
|
||||||
icon: Users
|
{ id: 'integration', icon: Layers },
|
||||||
},
|
{ id: 'impact', icon: BarChart3 },
|
||||||
{
|
] as const;
|
||||||
title: 'Bezpieczeństwo od początku',
|
export type WhyId = (typeof whyHumanAI)[number]['id'];
|
||||||
icon: ShieldCheck
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Integracja z istniejącymi narzędziami',
|
|
||||||
icon: Layers
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Mierzalny efekt biznesowy',
|
|
||||||
icon: BarChart3
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export const processSteps = [
|
export const processSteps = [
|
||||||
{ id: 1, title: 'Diagnoza', icon: Eye },
|
{ id: 'diagnosis', number: 1, icon: Eye },
|
||||||
{ id: 2, title: 'Projekt rozwiązania', icon: Layout },
|
{ id: 'design', number: 2, icon: Layout },
|
||||||
{ id: 3, title: 'Prototyp', icon: Zap },
|
{ id: 'prototype', number: 3, icon: Zap },
|
||||||
{ id: 4, title: 'Wdrożenie', icon: Rocket },
|
{ id: 'deployment', number: 4, icon: Rocket },
|
||||||
{ id: 5, title: 'Utrzymanie i rozwój', icon: Settings }
|
{ id: 'maintenance', number: 5, icon: Settings },
|
||||||
];
|
] as const;
|
||||||
|
export type StepId = (typeof processSteps)[number]['id'];
|
||||||
export const audience = [
|
|
||||||
'founderzy i małe zespoły',
|
|
||||||
'firmy usługowe',
|
|
||||||
'działy operacyjne',
|
|
||||||
'zespoły produktowe',
|
|
||||||
'software house’y i integratorzy'
|
|
||||||
];
|
|
||||||
|
|
|
||||||
253
src/i18n/en.ts
253
src/i18n/en.ts
|
|
@ -1,4 +1,118 @@
|
||||||
export const en = {
|
import type { Bullet, Rich } from './types';
|
||||||
|
import type { pl } from './pl';
|
||||||
|
|
||||||
|
export const en: typeof pl = {
|
||||||
|
nav: {
|
||||||
|
offerings: 'Services',
|
||||||
|
platform: 'Platform',
|
||||||
|
process: 'Process',
|
||||||
|
contact: 'Contact',
|
||||||
|
cta: 'Let\'s talk',
|
||||||
|
},
|
||||||
|
common: {
|
||||||
|
backToHome: '← Back to homepage',
|
||||||
|
},
|
||||||
|
hero: {
|
||||||
|
// Split so the accent span can wrap a different phrase per language.
|
||||||
|
titleBefore: 'We build the human ',
|
||||||
|
titleAccent: 'side',
|
||||||
|
titleAfter: ' of AI',
|
||||||
|
subtitle:
|
||||||
|
'We design and deploy AI systems that help people work faster, smarter, and with more peace of mind — from automation to multi-agent product platforms.',
|
||||||
|
ctaPrimary: 'Start with an AI audit',
|
||||||
|
ctaSecondary: 'See our services',
|
||||||
|
},
|
||||||
|
about: {
|
||||||
|
title: 'What is humanAI?',
|
||||||
|
lead: 'humanAI combines advisory, automation, product development, and our own agentic environment. We help you move from curiosity about AI to working processes, tools, and systems.',
|
||||||
|
points: [
|
||||||
|
'Strategy without the hype',
|
||||||
|
'Automation you can actually maintain',
|
||||||
|
'Agents under human control',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
offerings: {
|
||||||
|
title: 'Our Services',
|
||||||
|
lead: 'We build end-to-end solutions that turn the potential of artificial intelligence into real business outcomes.',
|
||||||
|
// Service names stay identical across languages, but are kept as per-language
|
||||||
|
// keys so they can diverge later without a refactor.
|
||||||
|
items: {
|
||||||
|
strategy: {
|
||||||
|
title: 'AI Strategy & Advisory',
|
||||||
|
description:
|
||||||
|
'We help you find real applications for AI in your business, choose priorities, and plan deployments without burning time on random experiments.',
|
||||||
|
points: ['process audit', 'use-case mapping', 'deployment prioritisation', 'AI safety policies'],
|
||||||
|
},
|
||||||
|
automation: {
|
||||||
|
title: 'AI Automation & Workflows',
|
||||||
|
description:
|
||||||
|
'We build automations that connect AI models with everyday tools: documents, email, CRM, databases, APIs, and internal processes.',
|
||||||
|
points: ['operational automation', 'API integrations', 'human-in-the-loop workflows', 'monitoring and logs'],
|
||||||
|
},
|
||||||
|
products: {
|
||||||
|
title: 'AI Products & Interfaces',
|
||||||
|
description:
|
||||||
|
'We design and build applications, dashboards, chatbots, copilots, and semantic search tools that turn AI into a genuinely useful user experience.',
|
||||||
|
points: ['web applications', 'AI dashboards', 'chatbots and copilots', 'RAG / knowledge search'],
|
||||||
|
},
|
||||||
|
multiagent: {
|
||||||
|
title: 'Multiagent Systems & Infrastructure',
|
||||||
|
description:
|
||||||
|
'We build multi-agent systems that can plan, execute tasks, use tools, and collaborate with humans while keeping control and auditability intact.',
|
||||||
|
points: ['agent orchestration', 'tool registry / MCP', 'approval gates', 'memory, logs & observability'],
|
||||||
|
linkLabel: 'See how it works',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
why: {
|
||||||
|
title: 'AI shouldn\'t be a black box',
|
||||||
|
lead: 'The best AI deployments are understandable, measurable, and safe. That\'s why we design systems that show their work, leave a decision trail, and let humans approve the steps that matter.',
|
||||||
|
items: {
|
||||||
|
hitl: 'Human-in-the-loop',
|
||||||
|
security: 'Security from day one',
|
||||||
|
integration: 'Integrates with what you already use',
|
||||||
|
impact: 'Measurable business impact',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
process: {
|
||||||
|
title: 'From idea to working system',
|
||||||
|
stepLabel: 'Step',
|
||||||
|
steps: {
|
||||||
|
diagnosis: 'Diagnosis',
|
||||||
|
design: 'Solution design',
|
||||||
|
prototype: 'Prototype',
|
||||||
|
deployment: 'Deployment',
|
||||||
|
maintenance: 'Maintenance & growth',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
audience: {
|
||||||
|
title: 'Who we work with',
|
||||||
|
items: [
|
||||||
|
'founders and small teams',
|
||||||
|
'service businesses',
|
||||||
|
'operations teams',
|
||||||
|
'product teams',
|
||||||
|
'software houses and integrators',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
cta: {
|
||||||
|
title: 'Got a process that should already run itself?',
|
||||||
|
lead: 'Tell us what you\'re doing manually, repetitively, or too slowly. We\'ll see if AI can make it simpler.',
|
||||||
|
quote: '"One process, one problem, or one idea is enough to start."',
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
tagline: 'the human side of AI — we build practical AI systems for people and organisations.',
|
||||||
|
navHeading: 'Navigation',
|
||||||
|
privacyHeading: 'Privacy',
|
||||||
|
privacyPolicy: 'Privacy Policy',
|
||||||
|
cookies: 'Cookies',
|
||||||
|
rights: 'All rights reserved.',
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
title: 'humanAI — the human side of AI',
|
||||||
|
description:
|
||||||
|
'Strategy, automation, AI products, and multi-agent systems designed with humans at the center.',
|
||||||
|
},
|
||||||
contact: {
|
contact: {
|
||||||
labelName: 'Name',
|
labelName: 'Name',
|
||||||
placeholderName: 'Your name',
|
placeholderName: 'Your name',
|
||||||
|
|
@ -25,7 +139,6 @@ export const en = {
|
||||||
title: 'Privacy Policy',
|
title: 'Privacy Policy',
|
||||||
lastUpdated: 'Last updated: June 2026',
|
lastUpdated: 'Last updated: June 2026',
|
||||||
intro: 'This privacy policy describes how humanAI (gethumanai.pl) processes personal data and protects the privacy of its users.',
|
intro: 'This privacy policy describes how humanAI (gethumanai.pl) processes personal data and protects the privacy of its users.',
|
||||||
backToHome: '← Back to homepage',
|
|
||||||
sections: [
|
sections: [
|
||||||
{
|
{
|
||||||
title: '1. Data Controller',
|
title: '1. Data Controller',
|
||||||
|
|
@ -53,4 +166,140 @@ export const en = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
platform: {
|
||||||
|
title: 'Overseeing agentic systems',
|
||||||
|
workingName: '(working name — to be confirmed before launch)',
|
||||||
|
watchers: {
|
||||||
|
title: 'Who watches the agents that watch everything else?',
|
||||||
|
paragraphs: [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
t: 'Automations and AI agents can work without a human — organising documents, controlling devices, reacting to events. But left unsupervised, sooner or later they\'ll do something nobody wanted. ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
t: 'We built a system that observes other systems — including other agentic systems',
|
||||||
|
em: 'strong',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
t: ' — it detects when something isn\'t working, diagnoses the cause, and fixes the problem itself. And wherever a decision is irreversible, it waits for a human.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'This isn\'t a concept. The same core has been running our own distributed infrastructure for months — and has been through real incidents, each of which made it smarter.',
|
||||||
|
] as Rich[],
|
||||||
|
},
|
||||||
|
problem: {
|
||||||
|
title: 'The problem that disappears',
|
||||||
|
bullets: [
|
||||||
|
{
|
||||||
|
lead: 'Autonomy without boundaries is a risk.',
|
||||||
|
body: ' Anything that can open a gate or change a service\'s configuration on its own needs a clear line: what it can do by itself, and what needs a human\'s go-ahead.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lead: 'Automation isn\'t intelligence.',
|
||||||
|
body: [
|
||||||
|
{ t: ' Many homes and businesses already have systems that ' },
|
||||||
|
{ t: 'execute', em: 'em' },
|
||||||
|
{ t: ' commands — lights, blinds, gates, document flows. The problem is that none of it ' },
|
||||||
|
{ t: 'understands', em: 'em' },
|
||||||
|
{ t: ': it doesn\'t connect facts, react to context, or learn.' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lead: 'Knowledge disappears.',
|
||||||
|
body: ' Why did something break a month ago, and what fixed it? That knowledge usually lives in one person\'s head, or in an unsearchable pile of documents.',
|
||||||
|
},
|
||||||
|
] as Bullet[],
|
||||||
|
},
|
||||||
|
howItWorks: {
|
||||||
|
title: 'How it works',
|
||||||
|
core: [
|
||||||
|
{ t: 'The supervisory core — one, shared by everything.', em: 'strong' },
|
||||||
|
{
|
||||||
|
t: ' A lightweight agent on every machine reports the state of its services. A central "brain" builds a full picture from that, detects deviations from what should be running, and either fixes the problem itself — if the action is safe — or asks a human if it isn\'t.',
|
||||||
|
},
|
||||||
|
] as Rich,
|
||||||
|
humanAsk: [
|
||||||
|
{
|
||||||
|
t: 'That "asking a human" part isn\'t theoretical. A real example: the system notices you\'ve left for the weekend and the blinds are still open. It won\'t close them on its own — it sends you a message: ',
|
||||||
|
},
|
||||||
|
{ t: '"You left the blinds open. Close them now?"', em: 'quote' },
|
||||||
|
{
|
||||||
|
t: ' You reply "yes" — and only then does the system hand that off to your existing home automation. The decision is always yours; the system handles the execution.',
|
||||||
|
},
|
||||||
|
] as Rich,
|
||||||
|
tailored: [
|
||||||
|
{ t: 'The working system — built around you.', em: 'strong' },
|
||||||
|
{ t: ' What this supervision watches over is tailored to what you actually need.' },
|
||||||
|
] as Rich,
|
||||||
|
home: {
|
||||||
|
title: 'For your home',
|
||||||
|
body: [
|
||||||
|
{
|
||||||
|
t: 'You already have an automated home — lights, blinds, gates, climate control — but that\'s automation, not intelligence: it does exactly what you tell it, and nothing more. We add a layer that ',
|
||||||
|
},
|
||||||
|
{ t: 'understands context', em: 'strong' },
|
||||||
|
{
|
||||||
|
t: ': it recognises your car by its license plate and opens the gate before you get there. It sees on your cameras that no one\'s home and dims the lights for the night on its own. It notices you\'ve left and something was left open — and asks before it does anything.',
|
||||||
|
},
|
||||||
|
] as Rich,
|
||||||
|
},
|
||||||
|
business: {
|
||||||
|
title: 'For your business',
|
||||||
|
body: 'You\'ve got a mess of documents, a paper trail nobody\'s happy with, and data scattered across someone else\'s cloud that you don\'t fully control. We build you a paperless system on your own servers — with your own domain and authentication, with no dependency on external cloud providers. We index documents, photos, and correspondence so they can be searched by meaning, not just keywords. The interface — a web dashboard always, and a chatbot too if it genuinely fits your business — is matched to what you actually need, not to whatever\'s trendy.' as Rich,
|
||||||
|
},
|
||||||
|
shared:
|
||||||
|
'Both paths share the same foundation: everything that\'s supposed to run is defined as code in a repository — every change has a history and can be rolled back — and both run under the same supervision that makes sure nothing fails silently.' as Rich,
|
||||||
|
},
|
||||||
|
underTheHood: {
|
||||||
|
title: 'Under the hood — for the curious',
|
||||||
|
bullets: [
|
||||||
|
{
|
||||||
|
body: [
|
||||||
|
{ t: 'A knowledge base shouldn\'t end up as a dead vector index. On top of semantic search, we build a ' },
|
||||||
|
{ t: 'readable, compiled wiki maintained by a language model', em: 'strong' },
|
||||||
|
{
|
||||||
|
t: ' — plain text files with cross-links between concepts, which the agent reads and updates the way a person maintains code. We build this on an open standard (Google OKF), not a closed format of our own — so the knowledge stays readable and portable.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
body: 'Queries first reach a cheaper model — a more expensive one only steps in when the task genuinely calls for it. In practice: summarising an entire document corpus once cost pennies instead of days of compute.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
body: 'Not everything needs to be online 24 hours a day. When the primary machine is asleep, queries automatically fail over to backup hardware — with no interruption the user would notice.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
body: 'The system tells the difference between "this service is down" and "this service is asleep as planned" — previously a common source of false alarms.',
|
||||||
|
},
|
||||||
|
] as Bullet[],
|
||||||
|
},
|
||||||
|
why: {
|
||||||
|
title: 'Why this, not ordinary monitoring or ordinary automation',
|
||||||
|
bullets: [
|
||||||
|
{ lead: 'We understand context, not just execute commands.' },
|
||||||
|
{ lead: 'We fix things, not just raise alarms.' },
|
||||||
|
{
|
||||||
|
lead: 'A human is genuinely in the loop',
|
||||||
|
body: ' — decisions are always yours, execution is the system\'s job.',
|
||||||
|
},
|
||||||
|
{ lead: 'Zero black box.', body: ' Every decision leaves a trail you can review later.' },
|
||||||
|
{
|
||||||
|
lead: 'Your data stays yours',
|
||||||
|
body: ' — your own server, your own domain, no dependency on someone else\'s cloud.',
|
||||||
|
},
|
||||||
|
] as Bullet[],
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
title: 'Honestly, where we are',
|
||||||
|
paragraphs: [
|
||||||
|
'The supervisory core runs our infrastructure every day — self-healing works end to end, alerts reach a phone, the knowledge base answers questions even when the primary hardware is asleep.',
|
||||||
|
'Dedicated deployments — for a specific home or business — we build as needed, on top of the same proven core. We\'d rather say plainly what we\'re still working on than promise more than what actually works.',
|
||||||
|
] as Rich[],
|
||||||
|
},
|
||||||
|
cta: {
|
||||||
|
title: 'Let\'s talk',
|
||||||
|
body: 'Tell us what you want to automate or organise — we\'ll figure out how to build it, and put it under the same supervision we run on ourselves.' as Rich,
|
||||||
|
button: 'Let\'s talk',
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
247
src/i18n/pl.ts
247
src/i18n/pl.ts
|
|
@ -1,4 +1,117 @@
|
||||||
|
import type { Bullet, Rich } from './types';
|
||||||
|
|
||||||
export const pl = {
|
export const pl = {
|
||||||
|
nav: {
|
||||||
|
offerings: 'Oferta',
|
||||||
|
platform: 'Platforma',
|
||||||
|
process: 'Proces',
|
||||||
|
contact: 'Kontakt',
|
||||||
|
cta: 'Porozmawiajmy',
|
||||||
|
},
|
||||||
|
common: {
|
||||||
|
backToHome: '← Wróć na stronę główną',
|
||||||
|
},
|
||||||
|
hero: {
|
||||||
|
// Split so the accent span can wrap a different phrase per language.
|
||||||
|
titleBefore: 'Budujemy ',
|
||||||
|
titleAccent: 'ludzką stronę',
|
||||||
|
titleAfter: ' AI',
|
||||||
|
subtitle:
|
||||||
|
'Projektujemy i wdrażamy systemy AI, które pomagają ludziom pracować szybciej, mądrzej i spokojniej — od automatyzacji po multiagentowe platformy produktowe.',
|
||||||
|
ctaPrimary: 'Zacznijmy od audytu AI',
|
||||||
|
ctaSecondary: 'Zobacz ofertę',
|
||||||
|
},
|
||||||
|
about: {
|
||||||
|
title: 'Czym jest humanAI?',
|
||||||
|
lead: 'humanAI łączy doradztwo, automatyzację, budowę produktów i własne środowisko agentowe. Pomagamy przejść od ciekawości wokół AI do działających procesów, narzędzi i systemów.',
|
||||||
|
points: [
|
||||||
|
'Strategia bez hype’u',
|
||||||
|
'Automatyzacje, które da się utrzymać',
|
||||||
|
'Agenci pod kontrolą człowieka',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
offerings: {
|
||||||
|
title: 'Nasza Oferta',
|
||||||
|
lead: 'Budujemy kompleksowe rozwiązania, które przekładają potencjał sztucznej inteligencji na realne wyniki biznesowe.',
|
||||||
|
// Service names stay identical across languages, but are kept as per-language
|
||||||
|
// keys so they can diverge later without a refactor.
|
||||||
|
items: {
|
||||||
|
strategy: {
|
||||||
|
title: 'AI Strategy & Advisory',
|
||||||
|
description:
|
||||||
|
'Pomagamy znaleźć realne zastosowania AI w firmie, wybrać priorytety i zaplanować wdrożenia bez przepalania czasu na przypadkowe eksperymenty.',
|
||||||
|
points: ['audyt procesów', 'mapa use case’ów', 'priorytetyzacja wdrożeń', 'polityki bezpieczeństwa AI'],
|
||||||
|
},
|
||||||
|
automation: {
|
||||||
|
title: 'AI Automation & Workflows',
|
||||||
|
description:
|
||||||
|
'Budujemy automatyzacje, które łączą modele AI z codziennymi narzędziami: dokumentami, mailem, CRM, bazami danych, API i wewnętrznymi procesami.',
|
||||||
|
points: ['automatyzacje operacyjne', 'integracje API', 'workflow z człowiekiem w pętli', 'monitoring i logi'],
|
||||||
|
},
|
||||||
|
products: {
|
||||||
|
title: 'AI Products & Interfaces',
|
||||||
|
description:
|
||||||
|
'Projektujemy i tworzymy aplikacje, panele, chatboty, copilots i wyszukiwarki semantyczne, które przekładają AI na użyteczne doświadczenie użytkownika.',
|
||||||
|
points: ['aplikacje webowe', 'panele AI', 'chatboty i copilots', 'RAG / wyszukiwanie w wiedzy'],
|
||||||
|
},
|
||||||
|
multiagent: {
|
||||||
|
title: 'Multiagent Systems & Infrastructure',
|
||||||
|
description:
|
||||||
|
'Tworzymy systemy wieloagentowe, które potrafią planować, wykonywać zadania, korzystać z narzędzi i współpracować z człowiekiem przy zachowaniu kontroli i audytowalności.',
|
||||||
|
points: ['agent orchestration', 'tool registry / MCP', 'approval gates', 'pamięć, logi i obserwowalność'],
|
||||||
|
linkLabel: 'Zobacz jak to działa',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
why: {
|
||||||
|
title: 'AI nie powinno być czarną skrzynką',
|
||||||
|
lead: 'Najlepsze wdrożenia AI są zrozumiałe, mierzalne i bezpieczne. Dlatego projektujemy systemy, które pokazują swoje działanie, zostawiają ślad decyzyjny i pozwalają człowiekowi zatwierdzać ważne kroki.',
|
||||||
|
items: {
|
||||||
|
hitl: 'Human-in-the-loop',
|
||||||
|
security: 'Bezpieczeństwo od początku',
|
||||||
|
integration: 'Integracja z istniejącymi narzędziami',
|
||||||
|
impact: 'Mierzalny efekt biznesowy',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
process: {
|
||||||
|
title: 'Od pomysłu do działającego systemu',
|
||||||
|
stepLabel: 'Krok',
|
||||||
|
steps: {
|
||||||
|
diagnosis: 'Diagnoza',
|
||||||
|
design: 'Projekt rozwiązania',
|
||||||
|
prototype: 'Prototyp',
|
||||||
|
deployment: 'Wdrożenie',
|
||||||
|
maintenance: 'Utrzymanie i rozwój',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
audience: {
|
||||||
|
title: 'Dla kogo',
|
||||||
|
items: [
|
||||||
|
'founderzy i małe zespoły',
|
||||||
|
'firmy usługowe',
|
||||||
|
'działy operacyjne',
|
||||||
|
'zespoły produktowe',
|
||||||
|
'software house’y i integratorzy',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
cta: {
|
||||||
|
title: 'Masz proces, który powinien już pracować sam?',
|
||||||
|
lead: 'Opowiedz, co robisz ręcznie, powtarzalnie albo zbyt wolno. Sprawdzimy, czy AI może to uprościć.',
|
||||||
|
quote: '"Na start wystarczy jeden proces, jeden problem albo jeden pomysł."',
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
tagline: 'ludzka strona AI — budujemy praktyczne systemy AI dla ludzi i organizacji.',
|
||||||
|
navHeading: 'Nawigacja',
|
||||||
|
privacyHeading: 'Prywatność',
|
||||||
|
privacyPolicy: 'Polityka prywatności',
|
||||||
|
cookies: 'Cookies',
|
||||||
|
rights: 'Wszelkie prawa zastrzeżone.',
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
title: 'humanAI — ludzka strona AI',
|
||||||
|
description:
|
||||||
|
'Strategia, automatyzacje, produkty AI i systemy multiagentowe projektowane z człowiekiem w centrum.',
|
||||||
|
},
|
||||||
contact: {
|
contact: {
|
||||||
labelName: 'Imię',
|
labelName: 'Imię',
|
||||||
placeholderName: 'Twoje imię',
|
placeholderName: 'Twoje imię',
|
||||||
|
|
@ -25,7 +138,6 @@ export const pl = {
|
||||||
title: 'Polityka prywatności',
|
title: 'Polityka prywatności',
|
||||||
lastUpdated: 'Ostatnia aktualizacja: czerwiec 2026',
|
lastUpdated: 'Ostatnia aktualizacja: czerwiec 2026',
|
||||||
intro: 'Niniejsza polityka prywatności opisuje, w jaki sposób humanAI (gethumanai.pl) przetwarza dane osobowe i dba o prywatność użytkowników.',
|
intro: 'Niniejsza polityka prywatności opisuje, w jaki sposób humanAI (gethumanai.pl) przetwarza dane osobowe i dba o prywatność użytkowników.',
|
||||||
backToHome: '← Wróć na stronę główną',
|
|
||||||
sections: [
|
sections: [
|
||||||
{
|
{
|
||||||
title: '1. Administrator danych',
|
title: '1. Administrator danych',
|
||||||
|
|
@ -53,4 +165,137 @@ export const pl = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
platform: {
|
||||||
|
title: 'Nadzór nad systemami agentowymi',
|
||||||
|
workingName: '(nazwa robocza — do potwierdzenia przed publikacją)',
|
||||||
|
watchers: {
|
||||||
|
title: 'Kto pilnuje agentów, którzy pilnują wszystkiego innego?',
|
||||||
|
paragraphs: [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
t: 'Automatyzacje i agenci AI potrafią pracować bez człowieka — porządkować dokumenty, sterować urządzeniami, reagować na zdarzenia. Ale zostawione bez nadzoru, prędzej czy później zrobią coś, czego nikt nie chciał. Zbudowaliśmy system, który ',
|
||||||
|
},
|
||||||
|
{ t: 'obserwuje inne systemy — w tym inne systemy agentowe', em: 'strong' },
|
||||||
|
{
|
||||||
|
t: ' — wykrywa, że coś nie działa, diagnozuje przyczynę i naprawia problem sam. A tam, gdzie decyzja jest nieodwracalna, czeka na człowieka.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'To nie koncept. Ten sam rdzeń od miesięcy prowadzi naszą własną, rozproszoną infrastrukturę — i przeszedł przez realne awarie, z których każda coś go nauczyła.',
|
||||||
|
] as Rich[],
|
||||||
|
},
|
||||||
|
problem: {
|
||||||
|
title: 'Problem, który znika',
|
||||||
|
bullets: [
|
||||||
|
{
|
||||||
|
lead: 'Autonomia bez ram to ryzyko.',
|
||||||
|
body: ' Coś, co może samo otworzyć bramę albo zmienić konfigurację usługi, potrzebuje jasnej granicy: co wolno zrobić samodzielnie, co wymaga zgody człowieka.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lead: 'Automatyka to nie inteligencja.',
|
||||||
|
body: [
|
||||||
|
{ t: ' Wiele domów i firm ma już systemy, które ' },
|
||||||
|
{ t: 'wykonują', em: 'em' },
|
||||||
|
{ t: ' polecenia — światła, rolety, bramy, obieg dokumentów. Problem w tym, że nic tego nie ' },
|
||||||
|
{ t: 'rozumie', em: 'em' },
|
||||||
|
{ t: ': nie kojarzy faktów, nie reaguje na kontekst, nie uczy się.' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lead: 'Wiedza znika.',
|
||||||
|
body: ' Dlaczego coś się zepsuło miesiąc temu i co wtedy pomogło? Zwykle ta wiedza siedzi w głowie jednej osoby albo w nieprzeszukiwalnym stosie dokumentów.',
|
||||||
|
},
|
||||||
|
] as Bullet[],
|
||||||
|
},
|
||||||
|
howItWorks: {
|
||||||
|
title: 'Jak to działa',
|
||||||
|
core: [
|
||||||
|
{ t: 'Rdzeń nadzoru — jeden, wspólny dla wszystkiego.', em: 'strong' },
|
||||||
|
{
|
||||||
|
t: ' Lekki agent na każdej maszynie zgłasza stan usług. Centralny „mózg" buduje z tego pełny obraz sytuacji, wykrywa odchylenia od tego, co powinno działać, i albo naprawia problem sam — jeśli akcja jest bezpieczna — albo pyta człowieka, jeśli nie jest.',
|
||||||
|
},
|
||||||
|
] as Rich,
|
||||||
|
humanAsk: [
|
||||||
|
{
|
||||||
|
t: 'To „pytanie człowieka" nie jest teorią. Przykład z życia: system zauważa, że wyjechałeś na weekend, a rolety zostały otwarte. Sam ich nie zamknie — wysyła Ci wiadomość: ',
|
||||||
|
},
|
||||||
|
{ t: '„Nie zamknąłeś rolet. Zamknąć teraz?"', em: 'quote' },
|
||||||
|
{
|
||||||
|
t: ' Odpisujesz „tak" — i dopiero wtedy system zleca to istniejącej automatyce domowej. Decyzja zawsze należy do Ciebie; wykonanie bierze na siebie system.',
|
||||||
|
},
|
||||||
|
] as Rich,
|
||||||
|
tailored: [
|
||||||
|
{ t: 'System roboczy — budowany pod Ciebie.', em: 'strong' },
|
||||||
|
{ t: ' To, co ten nadzór pilnuje, dopasowujemy do konkretnej potrzeby.' },
|
||||||
|
] as Rich,
|
||||||
|
home: {
|
||||||
|
title: 'Dla domu',
|
||||||
|
body: [
|
||||||
|
{
|
||||||
|
t: 'Masz już zautomatyzowany dom — światła, rolety, bramy, klimatyzację — ale to automatyka, nie inteligencja: robi dokładnie to, co jej każesz, i nic więcej. Dokładamy warstwę, która ',
|
||||||
|
},
|
||||||
|
{ t: 'rozumie kontekst', em: 'strong' },
|
||||||
|
{
|
||||||
|
t: ': rozpoznaje Twój samochód po tablicy rejestracyjnej i otwiera bramę, zanim dojedziesz do niej. Widzi na kamerach, że nikogo nie ma w domu, i sama wygasza światła na noc. Zauważa, że wyjechałeś, a coś zostało otwarte — i pyta, zanim cokolwiek zrobi.',
|
||||||
|
},
|
||||||
|
] as Rich,
|
||||||
|
},
|
||||||
|
business: {
|
||||||
|
title: 'Dla firm',
|
||||||
|
body: 'Masz bałagan w dokumentach, obieg papierowy, który nikogo nie satysfakcjonuje, i dane rozsiane po cudzych chmurach, nad którymi nie masz pełnej kontroli. Budujemy Ci paperless na Twoich własnych serwerach — z własną domeną i autoryzacją, bez uzależnienia od zewnętrznych dostawców chmury. Indeksujemy dokumenty, zdjęcia i korespondencję tak, żeby dało się to przeszukać po sensie, nie po słowach kluczowych. Interfejs — webowy panel, a jeśli to naprawdę pasuje do Twojej firmy, także chatbot — dobieramy do tego, czego faktycznie potrzebujesz, a nie do tego, co akurat modne.' as Rich,
|
||||||
|
},
|
||||||
|
shared:
|
||||||
|
'Obie ścieżki łączy to samo: cała konfiguracja tego, co ma działać, jest zapisana jako kod w repozytorium — każda zmiana ma historię i da się ją cofnąć — i obie działają pod tym samym nadzorem, który pilnuje, żeby nic nie zawiodło po cichu.' as Rich,
|
||||||
|
},
|
||||||
|
underTheHood: {
|
||||||
|
title: 'Pod maską — dla ciekawych',
|
||||||
|
bullets: [
|
||||||
|
{
|
||||||
|
body: [
|
||||||
|
{ t: 'Baza wiedzy nie kończy jako martwy indeks. Nad wyszukiwaniem semantycznym powstaje ' },
|
||||||
|
{ t: 'czytelna, kompilowana wiki utrzymywana przez model językowy', em: 'strong' },
|
||||||
|
{
|
||||||
|
t: ' — pliki tekstowe z linkami między pojęciami, które agent czyta i aktualizuje tak jak człowiek utrzymuje kod. Budujemy to na otwartym standardzie (Google OKF), nie na własnym, zamkniętym formacie — żeby wiedza, którą zbudujemy, zawsze zostawała czytelna i przenośna.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
body: 'Do zapytań najpierw sięga tańszy model — droższy dopiero wtedy, gdy sprawa naprawdę tego wymaga. W praktyce: streszczenie całego korpusu dokumentów potrafiło kosztować grosze zamiast dni obliczeń.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
body: 'Nie wszystko musi być online 24 godziny na dobę. Kiedy główna maszyna śpi, zapytania automatycznie przejmuje zapasowy sprzęt — bez przerwy zauważalnej dla użytkownika.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
body: 'System sam odróżnia „usługa nie działa" od „usługa śpi zgodnie z planem" — co wcześniej było częstym źródłem fałszywych alarmów.',
|
||||||
|
},
|
||||||
|
] as Bullet[],
|
||||||
|
},
|
||||||
|
why: {
|
||||||
|
title: 'Dlaczego to, a nie zwykły monitoring czy zwykła automatyka',
|
||||||
|
bullets: [
|
||||||
|
{ lead: 'Rozumiemy kontekst, nie tylko wykonujemy polecenia.' },
|
||||||
|
{ lead: 'Naprawiamy, nie tylko alarmujemy.' },
|
||||||
|
{
|
||||||
|
lead: 'Człowiek naprawdę jest w pętli',
|
||||||
|
body: ' — decyzje zawsze do Ciebie, wykonanie bierze na siebie system.',
|
||||||
|
},
|
||||||
|
{ lead: 'Zero czarnej skrzynki.', body: ' Każda decyzja zostawia ślad, który można później przejrzeć.' },
|
||||||
|
{
|
||||||
|
lead: 'Twoje dane zostają Twoje',
|
||||||
|
body: ' — własny serwer, własna domena, bez uzależnienia od cudzej chmury.',
|
||||||
|
},
|
||||||
|
] as Bullet[],
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
title: 'Uczciwie o tym, gdzie jesteśmy',
|
||||||
|
paragraphs: [
|
||||||
|
'Rdzeń nadzoru prowadzi naszą infrastrukturę na co dzień — samonaprawa działa od początku do końca, alerty trafiają na telefon, baza wiedzy odpowiada na pytania nawet gdy główny sprzęt śpi.',
|
||||||
|
'Dedykowane wdrożenia — pod konkretny dom czy firmę — budujemy w miarę potrzeb, opierając się na tym samym, sprawdzonym rdzeniu. Wolimy powiedzieć wprost, co jeszcze dopracowujemy, niż obiecywać więcej niż działa.',
|
||||||
|
] as Rich[],
|
||||||
|
},
|
||||||
|
cta: {
|
||||||
|
title: 'Umów rozmowę',
|
||||||
|
body: 'Powiedz, co chcesz zautomatyzować albo uporządkować — zobaczymy, jak to zbudować i objąć nadzorem, który sami na sobie testujemy.' as Rich,
|
||||||
|
button: 'Porozmawiajmy',
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
17
src/i18n/types.ts
Normal file
17
src/i18n/types.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
/**
|
||||||
|
* Shapes for copy that carries inline emphasis. Text with <strong>/<em> inside a
|
||||||
|
* sentence cannot be one flat string without either splitting the component or
|
||||||
|
* injecting HTML — so it is stored as a list of runs and rendered by <RichText>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type Run = {
|
||||||
|
t: string;
|
||||||
|
/** 'quote' is an <em> that brightens to full foreground — used for quoted speech. */
|
||||||
|
em?: 'strong' | 'em' | 'quote';
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Plain text, or a run list when the sentence needs inline emphasis. */
|
||||||
|
export type Rich = string | Run[];
|
||||||
|
|
||||||
|
/** List item rendered as an optional bold lead-in followed by the rest. */
|
||||||
|
export type Bullet = { lead?: string; body?: Rich };
|
||||||
Loading…
Reference in a new issue