diff --git a/CHANGELOG.md b/CHANGELOG.md index 9385b84..bed1af6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,58 @@ All notable changes to the FIVUCSAS platform. Dates are in ISO 8601 format. See each submodule's own `CHANGELOG.md` for granular per-repo changes. +## [2026-04-22] UI refresh — landing, web-app shell, BYS demo (Scope A) + +Zero functional change across the whole refresh. Every route, handler, +OAuth flow, postMessage event shape, widget integration call, `data-testid`, +and i18n key preserved. All 608 Vitest tests green in `web-app`. + +### `landing-website/` (fivucsas.com) +- Full rewrite of `src/App.tsx`, `src/index.css`, `tailwind.config.js`, + and `index.html`. New palette (violet primary, cyan secondary on dark + canvas), Space Grotesk display + Inter body + JetBrains Mono accents, + custom inline SVG icon system, working EN / TR toggle with localStorage + + `navigator.language` first-load detection. +- New sections: 10 auth-methods grid with custom icon glyphs, + architecture stack visual (Clients → Traefik → Services → Storage), + trust-signals row (RS256 default, AES-GCM-256, KVKK / GDPR, partitioned + audit logs, Retry-After MFA rate-limits), hosted-first CLI mock, refined + team + microservices + tech-stack sections. +- All JSON-LD Organization / WebSite / SoftwareApplication blocks, + robots.txt, sitemap.xml, og-image.png, pgp.asc, favicon.svg preserved. +- `.htaccess` (SPA fallback + security headers + caching) unchanged. + +### `bys-demo/` (demo.fivucsas.com) +- `styles.css` rewritten with refined tokens (Marmara crimson identity + retained, modern typography scale, red accent stripes on cards, premium + FIVUCSAS gradient CTA with shimmer sweep). Polished tables, schedule + items, GPA grid, callback loading + success + error states. +- **Zero HTML or JS edits** on `index.html`, `dashboard.html`, + `callback.html`: every ` - + - + - FIVUCSAS — Biyometrik Kimlik Doğrulama Platformu + FIVUCSAS — Biometric Identity Verification Platform + - - - - - - + - +
diff --git a/landing-website/src/App.tsx b/landing-website/src/App.tsx index da5caf4..1bd2eda 100644 --- a/landing-website/src/App.tsx +++ b/landing-website/src/App.tsx @@ -1,684 +1,828 @@ -import { motion, useScroll, useTransform } from 'framer-motion' -import { useEffect, useRef, useState } from 'react' +import { motion, useScroll, useTransform, useReducedMotion } from 'framer-motion' +import { useEffect, useMemo, useRef, useState } from 'react' + +// ──────────────────────────────────────────────────────────── +// i18n +// ──────────────────────────────────────────────────────────── + +type Lang = 'en' | 'tr' + +const t = { + nav: { + features: { en: 'Features', tr: 'Özellikler' }, + methods: { en: 'Auth Methods', tr: 'Yöntemler' }, + howItWorks: { en: 'How It Works', tr: 'Nasıl Çalışır' }, + architecture: { en: 'Architecture', tr: 'Mimari' }, + team: { en: 'Team', tr: 'Ekip' }, + demo: { en: 'Demo', tr: 'Demo' }, + status: { en: 'Status', tr: 'Durum' }, + signIn: { en: 'Admin Console',tr: 'Yönetici Paneli' }, + }, + hero: { + badge: { en: 'v1 shipped · 1,800+ tests · production-ready', tr: 'v1 yayında · 1.800+ test · üretime hazır' }, + titleA: { en: 'Identity verification', tr: 'Kimlik doğrulama' }, + titleB: { en: 'for the modern internet.', tr: 'modern internet için.' }, + lede: { + en: 'FIVUCSAS is an end-to-end biometric authentication platform. Ten auth methods, an N-step MFA dispatcher, OAuth 2.0 / OIDC hosted login, and a drop-in verification widget — for any web, mobile, or desktop app.', + tr: 'FIVUCSAS, uçtan uca biyometrik kimlik doğrulama platformudur. On doğrulama yöntemi, N-adımlı MFA akışı, OAuth 2.0 / OIDC barındırılan giriş ve her web, mobil veya masaüstü uygulamaya eklenebilen doğrulama widget’ı.', + }, + ctaPrimary: { en: 'Try the live demo', tr: 'Canlı demoyu dene' }, + ctaGhost: { en: 'Read the docs', tr: 'Belgeleri oku' }, + ctaGit: { en: 'View on GitHub', tr: 'GitHub’da gör' }, + }, + stats: { + methods: { en: 'Auth methods', tr: 'Doğrulama yöntemi' }, + endpoints: { en: 'API endpoints', tr: 'API uç noktası' }, + tests: { en: 'Tests passing', tr: 'Geçen test' }, + services: { en: 'Microservices', tr: 'Mikroservis' }, + }, + features: { + heading: { en: 'Built like infrastructure should be built.', tr: 'Altyapı nasıl kurulmalıysa öyle.' }, + sub: { en: 'Security, compliance, and developer experience — not a pick-two.', tr: 'Güvenlik, uyumluluk ve geliştirici deneyimi — üçü birden.' }, + items: [ + { + icon: 'shield', + title: { en: 'Liveness & anti-spoof', tr: 'Canlılık & spoof koruması' }, + body: { en: 'On-device liveness detection, screen-replay checks, rPPG pulse cues, and NFC document cross-reference.', tr: 'Cihaz üstü canlılık tespiti, ekran tekrarı kontrolleri, rPPG nabız sinyalleri ve NFC belge çapraz doğrulaması.' }, + }, + { + icon: 'lock', + title: { en: 'OAuth 2.0 · OIDC · PKCE', tr: 'OAuth 2.0 · OIDC · PKCE' }, + body: { en: 'Hosted-first login with full PKCE, discovery, JWKS, and refresh-token rotation. Works like Auth0 or Entra.', tr: 'Tam PKCE, keşif, JWKS ve yenileme jetonu rotasyonu ile barındırılan giriş. Auth0 veya Entra gibi çalışır.' }, + }, + { + icon: 'cube', + title: { en: 'Multi-tenant by design', tr: 'Çok kiracılı tasarım' }, + body: { en: 'Isolated data, per-tenant auth flows, and row-level security. Configure each tenant’s MFA independently.', tr: 'İzole veri, kiracı başına doğrulama akışları ve satır düzeyi güvenlik. Her kiracının MFA’sını bağımsız yapılandırın.' }, + }, + { + icon: 'bolt', + title: { en: 'N-step MFA dispatcher', tr: 'N-adımlı MFA dağıtıcısı' }, + body: { en: 'Chain any combination of the ten methods. JWT is deferred until every step clears. RFC 8176 amr claims emitted.', tr: 'On yöntemin herhangi bir kombinasyonunu zincirleyin. Tüm adımlar tamamlanmadan JWT verilmez. RFC 8176 amr talepleri.' }, + }, + { + icon: 'doc', + title: { en: 'Data export & KVKK/GDPR', tr: 'Veri dışa aktarma & KVKK/GDPR' }, + body: { en: 'Full personal-data export and purge per request. Audit logs are partitioned monthly for retention control.', tr: 'Talep üzerine tam kişisel veri dışa aktarımı ve silme. Denetim günlükleri aylık bölümlere ayrılmıştır.' }, + }, + { + icon: 'globe', + title: { en: 'Cross-platform SDKs', tr: 'Çapraz platform SDK’ları' }, + body: { en: 'Web iframe widget, Android & iOS via Kotlin Multiplatform, desktop via loopback (RFC 8252), and CLI.', tr: 'Web iframe widget’ı, Kotlin Multiplatform ile Android & iOS, loopback ile masaüstü (RFC 8252) ve CLI.' }, + }, + ], + }, + methods: { + heading: { en: 'Ten authentication methods, one platform.', tr: 'On kimlik doğrulama yöntemi, tek platform.' }, + sub: { en: 'Pick any subset — the flow builder composes them into a single verified session.', tr: 'İstediğiniz alt kümeyi seçin — akış düzenleyici bunları tek bir doğrulanmış oturumda birleştirir.' }, + items: [ + { key: 'face', icon: 'face', label: { en: 'Face recognition', tr: 'Yüz tanıma' } }, + { key: 'voice', icon: 'voice', label: { en: 'Voice verification',tr: 'Ses doğrulama' } }, + { key: 'finger', icon: 'finger', label: { en: 'Fingerprint / WebAuthn', tr: 'Parmak izi / WebAuthn' } }, + { key: 'hwkey', icon: 'key', label: { en: 'Hardware key', tr: 'Donanım anahtarı' } }, + { key: 'nfc', icon: 'nfc', label: { en: 'NFC document', tr: 'NFC belge' } }, + { key: 'totp', icon: 'totp', label: { en: 'TOTP / authenticator', tr: 'TOTP / kimlik doğrulayıcı' } }, + { key: 'sms', icon: 'sms', label: { en: 'SMS OTP', tr: 'SMS OTP' } }, + { key: 'email', icon: 'email', label: { en: 'Email OTP', tr: 'E-posta OTP' } }, + { key: 'qr', icon: 'qr', label: { en: 'QR code', tr: 'QR kod' } }, + { key: 'password', icon: 'pw', label: { en: 'Password', tr: 'Parola' } }, + ], + }, + how: { + heading: { en: 'Integrate in minutes. Ship the same day.', tr: 'Dakikalar içinde entegre edin. Aynı gün yayınlayın.' }, + sub: { en: 'Three redirects and a token exchange — no custom SDK required.', tr: 'Üç yönlendirme ve bir jeton takası — özel SDK gerekmez.' }, + steps: [ + { icon: 'sparkle', title: { en: 'Register tenant', tr: 'Kiracıyı kaydet' }, + body: { en: 'Create a tenant, pick your MFA flow, and copy the client_id + redirect URI.', tr: 'Bir kiracı oluşturun, MFA akışınızı seçin ve client_id + redirect URI’yi kopyalayın.' } }, + { icon: 'arrow', title: { en: 'Redirect users', tr: 'Kullanıcıları yönlendir' }, + body: { en: 'Call loginRedirect() from web, iOS, Android, desktop, or CLI. Your users land on verify.fivucsas.com.', tr: 'Web, iOS, Android, masaüstü veya CLI’dan loginRedirect() çağırın. Kullanıcılar verify.fivucsas.com’a gelir.' } }, + { icon: 'check', title: { en: 'Exchange & done', tr: 'Takas & bitir' }, + body: { en: 'Receive the authorization code at your callback. Exchange it for an access + ID token at /oauth2/token.', tr: 'Geri aramanızda yetkilendirme kodunu alın. /oauth2/token üzerinden access + ID jetonuyla takas edin.' } }, + ], + }, + architecture: { + heading: { en: 'Architecture, at a glance.', tr: 'Bir bakışta mimari.' }, + sub: { en: 'Hexagonal services, clean boundaries.', tr: 'Hexagonal servisler, net sınırlar.' }, + legendClients: { en: 'Clients', tr: 'İstemciler' }, + legendGateway: { en: 'Gateway', tr: 'Ağ Geçidi' }, + legendBackend: { en: 'Backend', tr: 'Arka Uç' }, + legendStorage: { en: 'Storage', tr: 'Depolama' }, + nodes: { + web: { en: 'Web SPA', tr: 'Web SPA' }, + mobile: { en: 'Mobile (KMP)', tr: 'Mobil (KMP)' }, + widget: { en: 'Verify Widget', tr: 'Doğrulama Widget’ı' }, + traefik: { en: 'Traefik v3.6 · TLS · routing', tr: 'Traefik v3.6 · TLS · yönlendirme' }, + identity: { en: 'Identity Core · Spring Boot · Java 21', tr: 'Identity Core · Spring Boot · Java 21' }, + biometric: { en: 'Biometric Processor · FastAPI · Python 3.12', tr: 'Biometric Processor · FastAPI · Python 3.12' }, + postgres: { en: 'PostgreSQL 17 + pgvector', tr: 'PostgreSQL 17 + pgvector' }, + redis: { en: 'Redis 7.4 · cache · sessions', tr: 'Redis 7.4 · önbellek · oturum' }, + }, + }, + services: { + heading: { en: 'Three services. Clear boundaries.', tr: 'Üç servis. Net sınırlar.' }, + sub: { en: 'Hexagonal architecture, ports and adapters throughout.', tr: 'Hexagonal mimari, uçtan uca ports & adapters.' }, + items: [ + { icon: 'lock', name: 'Identity Core API', + description: { en: 'Central authentication and identity service. Users, tenants, permissions, OAuth 2.0 / OIDC provider, MFA dispatcher.', tr: 'Merkezi kimlik doğrulama ve kimlik servisi. Kullanıcılar, kiracılar, izinler, OAuth 2.0 / OIDC sağlayıcı, MFA dağıtıcı.' }, + tech: ['Spring Boot 3.4.7', 'Java 21', 'PostgreSQL', 'Redis'] }, + { icon: 'eye', name: 'Biometric Processor', + description: { en: 'ML-powered face and voice recognition engine. 46+ endpoints for enrollment, verification, and anti-spoofing.', tr: 'ML destekli yüz ve ses tanıma motoru. Kayıt, doğrulama ve spoof önleme için 46+ uç nokta.' }, + tech: ['FastAPI', 'Python 3.12', 'InsightFace', 'pgvector'] }, + { icon: 'dash', name: 'Admin Dashboard', + description: { en: 'React app for tenants to manage users, flows, devices, and review real-time audit logs.', tr: 'Kiracıların kullanıcı, akış, cihaz yönetimi ve gerçek zamanlı denetim günlüklerini incelemesi için React uygulaması.' }, + tech: ['React 18', 'TypeScript', 'MUI', 'Redux Toolkit'] }, + ], + }, + tech: { + heading: { en: 'Built on proven foundations.', tr: 'Kanıtlanmış temeller üzerine inşa edildi.' }, + sub: { en: 'Boring where it counts, modern where it matters.', tr: 'Önemli yerde sıradan, gerekli yerde modern.' }, + }, + trust: { + heading: { en: 'Trust signals.', tr: 'Güven göstergeleri.' }, + items: [ + { k: { en: 'OIDC certified ready', tr: 'OIDC’e hazır' }, v: { en: 'RFC 6749 / OpenID Connect', tr: 'RFC 6749 / OpenID Connect' } }, + { k: { en: 'JWT signing', tr: 'JWT imzalama' }, v: { en: 'RS256 default + JWKS', tr: 'Varsayılan RS256 + JWKS' } }, + { k: { en: 'At-rest encryption', tr: 'Depoda şifreleme' }, v: { en: 'AES-GCM-256 per tenant', tr: 'Kiracı başına AES-GCM-256' } }, + { k: { en: 'Audit retention', tr: 'Denetim saklama' }, v: { en: 'Monthly partitioned logs', tr: 'Aylık bölümlenmiş günlükler' } }, + { k: { en: 'Data export & purge', tr: 'Veri dışa aktarma & silme' }, v: { en: 'KVKK / GDPR compliant', tr: 'KVKK / GDPR uyumlu' } }, + { k: { en: 'MFA rate-limiting', tr: 'MFA hız sınırı' }, v: { en: 'Retry-After + lockout', tr: 'Retry-After + kilitleme' } }, + ], + }, + team: { + heading: { en: 'Project team.', tr: 'Proje ekibi.' }, + sub: { en: 'Marmara University — Computer Engineering · CSE4297 / CSE4197', tr: 'Marmara Üniversitesi — Bilgisayar Mühendisliği · CSE4297 / CSE4197' }, + supervisor: { en: 'Supervisor: Assoc. Prof. Dr. Mustafa Ağaoğlu', tr: 'Danışman: Doç. Dr. Mustafa Ağaoğlu' }, + year: { en: 'Engineering Project · 2025–2026', tr: 'Mühendislik Projesi · 2025–2026' }, + }, + cta: { + heading: { en: 'Ready to ship real MFA?', tr: 'Gerçek MFA’yı yayınlamaya hazır mısınız?' }, + sub: { en: 'Try the admin console, explore the widget, or read the Swagger spec. Everything below is live.', tr: 'Yönetici konsolunu deneyin, widget’ı keşfedin veya Swagger spesifikasyonunu okuyun. Aşağıdaki her şey canlıdır.' }, + primary: { en: 'Open Admin Console', tr: 'Yönetici Konsolunu Aç' }, + ghost: { en: 'Swagger UI', tr: 'Swagger UI' }, + }, + footer: { + rights: { en: 'All rights reserved.', tr: 'Tüm hakları saklıdır.' }, + by: { en: 'A project by', tr: 'Bir' }, + byEnd: { en: ' · Marmara University', tr: ' projesi · Marmara Üniversitesi' }, + }, +} + +const useText = (lang: Lang) => (v: { en: string; tr: string }) => v[lang] + +// ──────────────────────────────────────────────────────────── +// Icons (inline SVG, 24×24) +// ──────────────────────────────────────────────────────────── + +const icons: Record = { + shield: (), + lock: (<>), + cube: (<>), + bolt: (), + doc: (<>), + globe: (<>), + face: (<>), + voice: (<>), + finger: (), + key: (<>), + nfc: (<>), + totp: (<>), + sms: (<>), + email: (<>), + qr: (<>), + pw: (<>), + sparkle:(<>), + arrow: (), + check: (), + eye: (<>), + dash: (<>), + github: (), +} -// ── Animated counter hook ── -function useCounter(target: number, duration = 2000) { +const Icon = ({ name, className = 'w-5 h-5', fill = false }: { name: keyof typeof icons; className?: string; fill?: boolean }) => ( + + {icons[name]} + +) + +// ──────────────────────────────────────────────────────────── +// Counter +// ──────────────────────────────────────────────────────────── + +function useCounter(target: number, duration = 1400) { const [count, setCount] = useState(0) const ref = useRef(null) const started = useRef(false) + const reduce = useReducedMotion() useEffect(() => { - const observer = new IntersectionObserver( - ([entry]) => { - if (entry.isIntersecting && !started.current) { - started.current = true - const start = performance.now() - const step = (now: number) => { - const elapsed = now - start - const progress = Math.min(elapsed / duration, 1) - setCount(Math.floor(progress * target)) - if (progress < 1) requestAnimationFrame(step) - } - requestAnimationFrame(step) + if (reduce) { setCount(target); return } + const el = ref.current + if (!el) return + const observer = new IntersectionObserver(([entry]) => { + if (entry.isIntersecting && !started.current) { + started.current = true + const start = performance.now() + const step = (now: number) => { + const progress = Math.min((now - start) / duration, 1) + const eased = 1 - Math.pow(1 - progress, 3) + setCount(Math.floor(eased * target)) + if (progress < 1) requestAnimationFrame(step) + else setCount(target) } - }, - { threshold: 0.3 }, - ) - if (ref.current) observer.observe(ref.current) + requestAnimationFrame(step) + } + }, { threshold: 0.4 }) + observer.observe(el) return () => observer.disconnect() - }, [target, duration]) + }, [target, duration, reduce]) return { count, ref } } -// ── Stat card ── -function StatCard({ value, suffix, label }: { value: number; suffix: string; label: string }) { +function Stat({ value, suffix, label }: { value: number; suffix: string; label: string }) { const { count, ref } = useCounter(value) return (
-
- {count} - {suffix} +
+ {count}{suffix}
-
{label}
+
{label}
) } -// ── Mobile menu state ── -function App() { +// ──────────────────────────────────────────────────────────── +// App +// ──────────────────────────────────────────────────────────── + +export default function App() { const [mobileOpen, setMobileOpen] = useState(false) + const [lang, setLang] = useState('en') + const text = useText(lang) + + // initial lang: localStorage, then browser, then en + useEffect(() => { + let initial: Lang = 'en' + try { + const saved = localStorage.getItem('fivucsas-lang') as Lang | null + if (saved === 'en' || saved === 'tr') initial = saved + else if ((navigator.language || 'en').toLowerCase().startsWith('tr')) initial = 'tr' + } catch { /* noop */ } + setLang(initial) + }, []) + + useEffect(() => { + document.documentElement.setAttribute('lang', lang) + document.documentElement.setAttribute('data-lang', lang) + document.title = lang === 'tr' + ? 'FIVUCSAS — Biyometrik Kimlik Doğrulama Platformu' + : 'FIVUCSAS — Biometric Identity Verification Platform' + try { localStorage.setItem('fivucsas-lang', lang) } catch { /* noop */ } + }, [lang]) + + const toggleLang = () => setLang(prev => prev === 'en' ? 'tr' : 'en') + const heroRef = useRef(null) const { scrollYProgress } = useScroll({ target: heroRef, offset: ['start start', 'end start'] }) - const heroOpacity = useTransform(scrollYProgress, [0, 1], [1, 0]) + const heroOpacity = useTransform(scrollYProgress, [0, 0.9], [1, 0]) const heroY = useTransform(scrollYProgress, [0, 1], [0, 80]) - // Close mobile menu on nav click - const navClick = () => setMobileOpen(false) + const [scrolled, setScrolled] = useState(false) + useEffect(() => { + const onScroll = () => setScrolled(window.scrollY > 24) + window.addEventListener('scroll', onScroll, { passive: true }) + onScroll() + return () => window.removeEventListener('scroll', onScroll) + }, []) + + const navItems = useMemo(() => ([ + { href: '#features', label: text(t.nav.features) }, + { href: '#methods', label: text(t.nav.methods) }, + { href: '#how-it-works', label: text(t.nav.howItWorks) }, + { href: '#architecture', label: text(t.nav.architecture) }, + { href: '#team', label: text(t.nav.team) }, + ]), [lang]) return ( -
- {/* ─── Skip to content (a11y) ─── */} - Skip to content - - {/* ─── Navigation ─── */} -
- - - {/* ─── Contact Section ─── */} -
-
- -

Get in Touch

-

- Interested in the project or want to learn more? Reach out through any of these channels. -

-
- - - - - - -
GitHub
-
Source Code
-
- - - - -
API Docs
-
Swagger UI
-
- - - - -
Email
-
Get in Touch
-
-
-
-
- - {/* ─── Footer ─── */} - +
) } -// ── Data ── +// ──────────────────────────────────────────────────────────── +// Sub-components +// ──────────────────────────────────────────────────────────── -const features = [ - { - icon: '\u{1F510}', - iconLabel: 'Locked padlock', - title: 'Biometric Authentication', - description: 'Advanced face recognition with liveness detection and anti-spoofing measures.', - }, - { - icon: '\u{1F3E2}', - iconLabel: 'Office building', - title: 'Multi-Tenant Architecture', - description: 'Isolated data and configurations per tenant with row-level security.', - }, - { - icon: '\u{1F465}', - iconLabel: 'People', - title: 'User Management', - description: 'Comprehensive user lifecycle management with RBAC permissions.', - }, - { - icon: '\u{1F4CA}', - iconLabel: 'Bar chart', - title: 'Audit Logging', - description: 'Complete audit trail for compliance and security monitoring.', - }, - { - icon: '\u{1F511}', - iconLabel: 'Key', - title: 'JWT Authentication', - description: 'Secure token-based authentication with refresh token rotation.', - }, - { - icon: '\u{1F4F1}', - iconLabel: 'Mobile phone', - title: 'Cross-Platform SDKs', - description: 'Native support for Android, iOS, and Desktop applications.', - }, -] +function SectionHead({ kicker, title, sub, compact = false }: { kicker: string; title: string; sub?: string; compact?: boolean }) { + return ( + + + + {kicker} + +

+ {title} +

+ {sub &&

{sub}

} +
+ ) +} -const howItWorks = [ - { - title: 'Register', - description: 'Create a tenant account and configure your authentication flow with the methods you need.', - icon: '\u{1F4DD}', - iconLabel: 'Memo', - }, - { - title: 'Enroll Biometric', - description: 'Users enroll their face, voice, or other biometric data through secure capture.', - icon: '\u{1F9D1}', - iconLabel: 'Person', - }, - { - title: 'Authenticate', - description: 'Verify identity in real-time using multi-step flows combining any of 10 auth methods.', - icon: '\u{2705}', - iconLabel: 'Check mark', - }, -] +function ArchLayer({ label, single, children }: { label: string; single?: boolean; children: React.ReactNode }) { + return ( +
+
{label}
+
+ {children} +
+
+ ) +} -const techStack = [ - { logo: 'https://cdn.jsdelivr.net/gh/devicons/devicon/icons/spring/spring-original.svg', name: 'Spring Boot', description: 'Identity Core API' }, - { logo: 'https://cdn.jsdelivr.net/gh/devicons/devicon/icons/fastapi/fastapi-original.svg', name: 'FastAPI', description: 'Biometric Processor' }, - { logo: 'https://cdn.jsdelivr.net/gh/devicons/devicon/icons/react/react-original.svg', name: 'React', description: 'Admin Dashboard' }, - { logo: 'https://cdn.jsdelivr.net/gh/devicons/devicon/icons/kotlin/kotlin-original.svg', name: 'Kotlin', description: 'Mobile/Desktop Apps' }, - { logo: 'https://cdn.jsdelivr.net/gh/devicons/devicon/icons/postgresql/postgresql-original.svg', name: 'PostgreSQL', description: 'Primary Database' }, - { logo: 'https://cdn.jsdelivr.net/gh/devicons/devicon/icons/redis/redis-original.svg', name: 'Redis', description: 'Cache & Queue' }, - { logo: 'https://cdn.jsdelivr.net/gh/devicons/devicon/icons/docker/docker-original.svg', name: 'Docker', description: 'Containerization' }, - { logo: 'https://cdn.jsdelivr.net/gh/devicons/devicon/icons/traefikproxy/traefikproxy-original.svg', name: 'Traefik', description: 'API Gateway + TLS' }, -] +function ArchNode({ icon, title, accent = false }: { icon: keyof typeof icons; title: string; accent?: boolean }) { + return ( +
+
+ +
+
{title}
+
+ ) +} -const services = [ - { - icon: '\u{1F510}', - iconLabel: 'Locked padlock', - name: 'Identity Core API', - description: 'Central authentication and identity management service handling users, tenants, and permissions.', - tech: ['Spring Boot 3.4.7', 'Java 21', 'PostgreSQL', 'Redis'], - }, - { - icon: '\u{1F441}\uFE0F', - iconLabel: 'Eye', - name: 'Biometric Processor', - description: 'ML-powered face recognition engine with 46+ endpoints for enrollment, verification, and analysis.', - tech: ['FastAPI', 'Python 3.12', 'InsightFace', 'pgvector'], - }, - { - icon: '\u{1F5A5}\uFE0F', - iconLabel: 'Desktop computer', - name: 'Admin Dashboard', - description: 'Feature-rich web application for tenant administrators to manage users and monitor activity.', - tech: ['React 18', 'TypeScript', 'MUI', 'Redux Toolkit'], - }, +// ──────────────────────────────────────────────────────────── +// Data +// ──────────────────────────────────────────────────────────── + +const techStack = [ + { logo: 'https://cdn.jsdelivr.net/gh/devicons/devicon/icons/spring/spring-original.svg', name: 'Spring Boot' }, + { logo: 'https://cdn.jsdelivr.net/gh/devicons/devicon/icons/fastapi/fastapi-original.svg', name: 'FastAPI' }, + { logo: 'https://cdn.jsdelivr.net/gh/devicons/devicon/icons/react/react-original.svg', name: 'React' }, + { logo: 'https://cdn.jsdelivr.net/gh/devicons/devicon/icons/kotlin/kotlin-original.svg', name: 'Kotlin' }, + { logo: 'https://cdn.jsdelivr.net/gh/devicons/devicon/icons/postgresql/postgresql-original.svg', name: 'PostgreSQL' }, + { logo: 'https://cdn.jsdelivr.net/gh/devicons/devicon/icons/redis/redis-original.svg', name: 'Redis' }, + { logo: 'https://cdn.jsdelivr.net/gh/devicons/devicon/icons/docker/docker-original.svg', name: 'Docker' }, + { logo: 'https://cdn.jsdelivr.net/gh/devicons/devicon/icons/traefikproxy/traefikproxy-original.svg', name: 'Traefik' }, ] const teamMembers = [ - { - initials: 'AG', - name: 'Ahmet Abdullah Gültekin', - role: 'Project Lead · Full-Stack Developer', - scope: 'Architecture · Backend · Frontend · Mobile · Biometrics · ML · DevOps', - gradient: 'from-blue-500 to-violet-500', - }, - { - initials: 'AE', - name: 'Ayşe Gülsüm Eren', - role: 'Mobile & Puzzle Developer', - scope: 'Kotlin Multiplatform · Biometric Puzzles · Hand Tracking', - gradient: 'from-fuchsia-500 to-pink-500', - }, - { - initials: 'AA', - name: 'Ayşenur Arıcı', - role: 'ML & Vision Researcher', - scope: 'YOLO Training · Liveness · Anti-Spoofing', - gradient: 'from-emerald-500 to-teal-500', - }, + { initials: 'AG', name: 'Ahmet Abdullah Gültekin', role: 'Project Lead · Full-Stack', scope: 'Architecture · Backend · Frontend · Mobile · Biometrics · ML · DevOps', gradient: 'from-primary-500 to-accent-500' }, + { initials: 'AE', name: 'Ayşe Gülsüm Eren', role: 'Mobile & Puzzle Developer', scope: 'Kotlin Multiplatform · Biometric Puzzles · Hand Tracking', gradient: 'from-fuchsia-500 to-pink-500' }, + { initials: 'AA', name: 'Ayşenur Arıcı', role: 'ML & Vision Researcher', scope: 'YOLO Training · Liveness · Anti-Spoofing', gradient: 'from-emerald-500 to-teal-500' }, ] - -export default App diff --git a/landing-website/src/index.css b/landing-website/src/index.css index 6213b17..8bbc095 100644 --- a/landing-website/src/index.css +++ b/landing-website/src/index.css @@ -2,39 +2,120 @@ @tailwind components; @tailwind utilities; +:root { + color-scheme: dark; +} + +html { + scroll-behavior: smooth; +} + body { font-family: 'Inter', system-ui, sans-serif; + background: #070713; + color: #e6e6f0; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; + font-feature-settings: 'ss01', 'ss02', 'cv01', 'cv09'; + overflow-x: hidden; } @layer utilities { .gradient-text { - @apply bg-clip-text text-transparent bg-gradient-to-r from-primary-500 to-accent-500; + @apply bg-clip-text text-transparent; + background-image: linear-gradient(135deg, #ffffff 0%, #a5b4fc 35%, #22d3ee 80%); } - - .gradient-bg { - @apply bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900; + .gradient-text-accent { + @apply bg-clip-text text-transparent; + background-image: linear-gradient(135deg, #a78bfa, #6366f1); + } + .glass { + background: rgba(17, 17, 32, 0.6); + backdrop-filter: blur(20px) saturate(1.4); + -webkit-backdrop-filter: blur(20px) saturate(1.4); + border: 1px solid rgba(99, 102, 241, 0.15); + } + .dot-grid { + background-image: radial-gradient(rgba(148, 163, 184, 0.18) 1px, transparent 1px); + background-size: 24px 24px; + } + .mask-fade-b { + -webkit-mask-image: linear-gradient(to bottom, black 60%, transparent 100%); + mask-image: linear-gradient(to bottom, black 60%, transparent 100%); + } + .ring-glow { + box-shadow: 0 0 0 1px rgba(99, 102, 241, 0.4), 0 20px 60px -20px rgba(99, 102, 241, 0.55); } } -/* Slow ambient pulse for background blobs */ -@keyframes pulse-slow { - 0%, 100% { opacity: 0.6; transform: scale(1); } - 50% { opacity: 1; transform: scale(1.08); } +/* Selection + scrollbar */ +::selection { + background-color: rgba(99, 102, 241, 0.35); + color: #fff; } +::-webkit-scrollbar { width: 10px; height: 10px; } +::-webkit-scrollbar-track { background: #070713; } +::-webkit-scrollbar-thumb { background: #2a2a40; border-radius: 10px; border: 2px solid #070713; } +::-webkit-scrollbar-thumb:hover { background: #3b3b56; } -.animate-pulse-slow { - animation: pulse-slow 6s ease-in-out infinite; +/* Ambient aurora background */ +.aurora { + position: fixed; + inset: 0; + pointer-events: none; + z-index: 0; + overflow: hidden; +} +.aurora::before, +.aurora::after { + content: ''; + position: absolute; + border-radius: 50%; + filter: blur(130px); + opacity: 0.55; +} +.aurora::before { + width: 640px; + height: 640px; + top: -200px; + right: -140px; + background: radial-gradient(circle, rgba(99, 102, 241, 0.35), transparent 70%); + animation: drift-a 28s ease-in-out infinite alternate; +} +.aurora::after { + width: 520px; + height: 520px; + bottom: 10%; + left: -180px; + background: radial-gradient(circle, rgba(34, 211, 238, 0.22), transparent 70%); + animation: drift-b 34s ease-in-out infinite alternate; +} +@keyframes drift-a { + 0% { transform: translate(0, 0) scale(1); } + 100% { transform: translate(-80px, 60px) scale(1.2); } +} +@keyframes drift-b { + 0% { transform: translate(0, 0) scale(1); } + 100% { transform: translate(60px, -90px) scale(1.2); } } -/* Smooth scroll */ -html { - scroll-behavior: smooth; +/* Noise texture overlay */ +.noise::before { + content: ''; + position: fixed; + inset: 0; + pointer-events: none; + z-index: 1; + opacity: 0.03; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.5 0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E"); } -/* Selection color */ -::selection { - background-color: rgba(59, 130, 246, 0.3); - color: white; +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } } diff --git a/landing-website/tailwind.config.js b/landing-website/tailwind.config.js index e22e42d..c8cb158 100644 --- a/landing-website/tailwind.config.js +++ b/landing-website/tailwind.config.js @@ -8,25 +8,79 @@ export default { extend: { colors: { primary: { - 50: '#eff6ff', - 100: '#dbeafe', - 200: '#bfdbfe', - 300: '#93c5fd', - 400: '#60a5fa', - 500: '#3b82f6', - 600: '#2563eb', - 700: '#1d4ed8', - 800: '#1e40af', - 900: '#1e3a8a', + 50: '#eef2ff', + 100: '#e0e7ff', + 200: '#c7d2fe', + 300: '#a5b4fc', + 400: '#818cf8', + 500: '#6366f1', + 600: '#4f46e5', + 700: '#4338ca', + 800: '#3730a3', + 900: '#312e81', }, accent: { 400: '#a78bfa', 500: '#8b5cf6', 600: '#7c3aed', - } + }, + cyan: { + 400: '#22d3ee', + 500: '#06b6d4', + 600: '#0891b2', + }, + trust: { + 400: '#34d399', + 500: '#10b981', + 600: '#059669', + }, + // custom canvas tokens + canvas: { + DEFAULT: '#070713', + sub: '#0c0c1a', + card: '#11112099', + border: '#1e1e2f', + borderStrong: '#2e2e44', + }, }, fontFamily: { sans: ['Inter', 'system-ui', 'sans-serif'], + display: ['"Space Grotesk"', 'Inter', 'sans-serif'], + mono: ['"JetBrains Mono"', 'ui-monospace', 'Menlo', 'monospace'], + }, + boxShadow: { + 'glow-primary': '0 10px 40px rgba(99, 102, 241, 0.35)', + 'glow-accent': '0 10px 40px rgba(139, 92, 246, 0.35)', + 'soft': '0 8px 32px rgba(0, 0, 0, 0.25)', + }, + backgroundImage: { + 'grid-slate': "linear-gradient(rgba(99,102,241,0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(99,102,241,0.05) 1px, transparent 1px)", + 'radial-primary': "radial-gradient(circle at center, rgba(99,102,241,0.2) 0%, transparent 60%)", + }, + animation: { + 'pulse-slow': 'pulse-slow 6s ease-in-out infinite', + 'float': 'float 8s ease-in-out infinite', + 'scan': 'scan 2.4s ease-in-out infinite', + 'shimmer': 'shimmer 3s linear infinite', + }, + keyframes: { + 'pulse-slow': { + '0%, 100%': { opacity: '0.55', transform: 'scale(1)' }, + '50%': { opacity: '0.95', transform: 'scale(1.08)' }, + }, + 'float': { + '0%, 100%': { transform: 'translateY(0px)' }, + '50%': { transform: 'translateY(-12px)' }, + }, + 'scan': { + '0%': { transform: 'translateY(-110%)' }, + '50%': { transform: 'translateY(110%)' }, + '100%': { transform: 'translateY(-110%)' }, + }, + 'shimmer': { + '0%': { backgroundPosition: '-200% 0' }, + '100%': { backgroundPosition: '200% 0' }, + }, }, }, }, diff --git a/web-app b/web-app index 4e4cdd3..ff17a57 160000 --- a/web-app +++ b/web-app @@ -1 +1 @@ -Subproject commit 4e4cdd393d3ac0a471f2661f83e8aeb0a2da588c +Subproject commit ff17a57c3fc3e8537d9754b4df94dcb4cb9fe983