mirror of
https://github.com/shadoll/vancouver.git
synced 2026-08-28 11:33:14 +00:00
feat: initial commit of Vancouver Investment Fund web application
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import router from 'svelte-spa-router';
|
||||
import { theme, language } from './stores.js';
|
||||
|
||||
import Header from './components/Header.svelte';
|
||||
import Footer from './components/Footer.svelte';
|
||||
import Home from './components/Home.svelte';
|
||||
|
||||
// Routes configuration
|
||||
const routes = {
|
||||
'/': Home,
|
||||
// Add more routes here in the future
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
// Initialize theme and language
|
||||
theme.init();
|
||||
language.init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
</svelte:head>
|
||||
|
||||
<Header />
|
||||
|
||||
<main class="main-content">
|
||||
<svelte:component this={router} {routes} />
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
|
||||
<style>
|
||||
.main-content {
|
||||
min-height: calc(100vh - 8rem);
|
||||
}
|
||||
</style>
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
/* CSS Custom Properties for theming */
|
||||
:root {
|
||||
/* Light theme */
|
||||
--primary-color: #2563eb;
|
||||
--primary-hover: #1d4ed8;
|
||||
--bg-color: #ffffff;
|
||||
--bg-secondary: #f8fafc;
|
||||
--bg-hover: #f1f5f9;
|
||||
--text-color: #1e293b;
|
||||
--text-secondary: #64748b;
|
||||
--border-color: #e2e8f0;
|
||||
--shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
/* Dark theme - Grey palette */
|
||||
--primary-color: #9ca3af;
|
||||
--primary-hover: #6b7280;
|
||||
--bg-color: #111827;
|
||||
--bg-secondary: #1f2937;
|
||||
--bg-hover: #374151;
|
||||
--text-color: #f9fafb;
|
||||
--text-secondary: #d1d5db;
|
||||
--border-color: #374151;
|
||||
--shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.3), 0 1px 2px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* Reset and base styles */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: var(--text-color);
|
||||
background-color: var(--bg-color);
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
text-decoration: none;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--primary-hover);
|
||||
}
|
||||
|
||||
/* Scrollbar styling */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--border-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* Focus styles for accessibility */
|
||||
button:focus-visible,
|
||||
a:focus-visible {
|
||||
outline: 2px solid var(--primary-color);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Loading animation */
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.loading {
|
||||
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
}
|
||||
|
||||
/* Responsive breakpoints */
|
||||
@media (max-width: 640px) {
|
||||
html {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 641px) and (max-width: 1024px) {
|
||||
html {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1025px) {
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print styles */
|
||||
@media print {
|
||||
* {
|
||||
color: black !important;
|
||||
background: white !important;
|
||||
}
|
||||
|
||||
.header,
|
||||
.footer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="26.6" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 308"><path fill="#FF3E00" d="M239.682 40.707C211.113-.182 154.69-12.301 113.895 13.69L42.247 59.356a82.198 82.198 0 0 0-37.135 55.056a86.566 86.566 0 0 0 8.536 55.576a82.425 82.425 0 0 0-12.296 30.719a87.596 87.596 0 0 0 14.964 66.244c28.574 40.893 84.997 53.007 125.787 27.016l71.648-45.664a82.182 82.182 0 0 0 37.135-55.057a86.601 86.601 0 0 0-8.53-55.577a82.409 82.409 0 0 0 12.29-30.718a87.573 87.573 0 0 0-14.963-66.244"></path><path fill="#FFF" d="M106.889 270.841c-23.102 6.007-47.497-3.036-61.103-22.648a52.685 52.685 0 0 1-9.003-39.85a49.978 49.978 0 0 1 1.713-6.693l1.35-4.115l3.671 2.697a92.447 92.447 0 0 0 28.036 14.007l2.663.808l-.245 2.659a16.067 16.067 0 0 0 2.89 10.656a17.143 17.143 0 0 0 18.397 6.828a15.786 15.786 0 0 0 4.403-1.935l71.67-45.672a14.922 14.922 0 0 0 6.734-9.977a15.923 15.923 0 0 0-2.713-12.011a17.156 17.156 0 0 0-18.404-6.832a15.78 15.78 0 0 0-4.396 1.933l-27.35 17.434a52.298 52.298 0 0 1-14.553 6.391c-23.101 6.007-47.497-3.036-61.101-22.649a52.681 52.681 0 0 1-9.004-39.849a49.428 49.428 0 0 1 22.34-33.114l71.664-45.677a52.218 52.218 0 0 1 14.563-6.398c23.101-6.007 47.497 3.036 61.101 22.648a52.685 52.685 0 0 1 9.004 39.85a50.559 50.559 0 0 1-1.713 6.692l-1.35 4.116l-3.67-2.693a92.373 92.373 0 0 0-28.037-14.013l-2.664-.809l.246-2.658a16.099 16.099 0 0 0-2.89-10.656a17.143 17.143 0 0 0-18.398-6.828a15.786 15.786 0 0 0-4.402 1.935l-71.67 45.674a14.898 14.898 0 0 0-6.73 9.975a15.9 15.9 0 0 0 2.709 12.012a17.156 17.156 0 0 0 18.404 6.832a15.841 15.841 0 0 0 4.402-1.935l27.345-17.427a52.147 52.147 0 0 1 14.552-6.397c23.101-6.006 47.497 3.037 61.102 22.65a52.681 52.681 0 0 1 9.003 39.848a49.453 49.453 0 0 1-22.34 33.12l-71.664 45.673a52.218 52.218 0 0 1-14.563 6.398"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,104 @@
|
||||
<script>
|
||||
import { language } from '../stores.js';
|
||||
import { t } from '../translations.js';
|
||||
</script>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<div class="footer-content">
|
||||
<div class="footer-section">
|
||||
<h3>Vancouver Investment Fund</h3>
|
||||
<p class="footer-description">
|
||||
{t('description', $language)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="footer-section">
|
||||
<h4>{t('contact_title', $language)}</h4>
|
||||
<div class="contact-info">
|
||||
<p><strong>{t('manager_title', $language)}</strong></p>
|
||||
<p>{t('address', $language)}</p>
|
||||
<p>{t('phone', $language)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<p>© 2025 Vancouver Investment Fund. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<style>
|
||||
.footer {
|
||||
background: var(--bg-secondary);
|
||||
border-top: 1px solid var(--border-color);
|
||||
padding: 3rem 0 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
gap: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.footer-section h3 {
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.footer-section h4 {
|
||||
color: var(--text-color);
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.footer-description {
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.contact-info p {
|
||||
margin: 0.5rem 0;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.footer-bottom {
|
||||
border-top: 1px solid var(--border-color);
|
||||
padding-top: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer-bottom p {
|
||||
margin: 0;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 2rem 0 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,237 @@
|
||||
<script>
|
||||
import { theme, language } from '../stores.js';
|
||||
import { Sun, Moon, Globe, Monitor } from 'lucide-svelte';
|
||||
|
||||
let showLanguageMenu = false;
|
||||
|
||||
function cycleTheme() {
|
||||
theme.cycle();
|
||||
}
|
||||
|
||||
function setLanguage(lang) {
|
||||
language.set(lang);
|
||||
showLanguageMenu = false;
|
||||
}
|
||||
|
||||
function toggleLanguageMenu() {
|
||||
showLanguageMenu = !showLanguageMenu;
|
||||
}
|
||||
|
||||
function getThemeIcon(currentTheme) {
|
||||
switch (currentTheme) {
|
||||
case 'light':
|
||||
return Sun;
|
||||
case 'dark':
|
||||
return Moon;
|
||||
case 'system':
|
||||
default:
|
||||
return Monitor;
|
||||
}
|
||||
}
|
||||
|
||||
function getThemeLabel(currentTheme) {
|
||||
switch (currentTheme) {
|
||||
case 'light':
|
||||
return 'Light';
|
||||
case 'dark':
|
||||
return 'Dark';
|
||||
case 'system':
|
||||
default:
|
||||
return 'System';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<header class="header">
|
||||
<div class="container">
|
||||
<div class="header-content">
|
||||
<div class="logo">
|
||||
<h1>Vancouver</h1>
|
||||
</div>
|
||||
|
||||
<div class="header-controls">
|
||||
<!-- Language Switcher -->
|
||||
<div class="language-switcher" class:active={showLanguageMenu}>
|
||||
<button
|
||||
class="control-btn language-btn"
|
||||
on:click={toggleLanguageMenu}
|
||||
aria-label="Change language"
|
||||
>
|
||||
<Globe size={20} />
|
||||
<span class="current-lang">{$language.toUpperCase()}</span>
|
||||
</button>
|
||||
|
||||
{#if showLanguageMenu}
|
||||
<div class="language-menu">
|
||||
<button on:click={() => setLanguage('en')} class:active={$language === 'en'}>
|
||||
English
|
||||
</button>
|
||||
<button on:click={() => setLanguage('uk')} class:active={$language === 'uk'}>
|
||||
Українська
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Theme Switcher -->
|
||||
<button
|
||||
class="control-btn theme-btn"
|
||||
on:click={cycleTheme}
|
||||
aria-label="Change theme mode"
|
||||
title="Current: {getThemeLabel($theme)}"
|
||||
>
|
||||
<svelte:component this={getThemeIcon($theme)} size={20} />
|
||||
<span class="theme-label">{getThemeLabel($theme)}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
.header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--bg-color);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
z-index: 1000;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
.logo h1 {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--primary-color);
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.header-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 0.5rem;
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.control-btn:hover {
|
||||
background: var(--bg-hover);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.language-switcher {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.language-btn {
|
||||
min-width: 4rem;
|
||||
}
|
||||
|
||||
.theme-btn {
|
||||
min-width: 5rem;
|
||||
}
|
||||
|
||||
.current-lang {
|
||||
font-weight: 500;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.theme-label {
|
||||
font-weight: 500;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.language-menu {
|
||||
position: absolute;
|
||||
top: calc(100% + 0.5rem);
|
||||
right: 0;
|
||||
background: var(--bg-color);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
animation: slideDown 0.2s ease;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.language-menu button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0.75rem 1rem;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-color);
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.language-menu button:hover {
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
|
||||
.language-menu button.active {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.logo h1 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.current-lang,
|
||||
.theme-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.language-btn,
|
||||
.theme-btn {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,473 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { language } from '../stores.js';
|
||||
import { t } from '../translations.js';
|
||||
import { TrendingUp, Building2, TreePine, ShoppingCart, Truck, Users } from 'lucide-svelte';
|
||||
|
||||
let mounted = false;
|
||||
|
||||
onMount(() => {
|
||||
mounted = true;
|
||||
|
||||
// Intersection Observer for scroll animations
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('animate-in');
|
||||
}
|
||||
});
|
||||
}, {
|
||||
threshold: 0.1,
|
||||
rootMargin: '0px 0px -50px 0px'
|
||||
});
|
||||
|
||||
const sections = document.querySelectorAll('.scroll-animate');
|
||||
sections.forEach(section => observer.observe(section));
|
||||
|
||||
return () => {
|
||||
sections.forEach(section => observer.unobserve(section));
|
||||
};
|
||||
});
|
||||
|
||||
const investmentAreas = [
|
||||
{
|
||||
icon: TrendingUp,
|
||||
titleKey: 'energy',
|
||||
descKey: 'energy_desc',
|
||||
color: '#059669'
|
||||
},
|
||||
{
|
||||
icon: TreePine,
|
||||
titleKey: 'woodworking',
|
||||
descKey: 'woodworking_desc',
|
||||
color: '#7c3aed'
|
||||
},
|
||||
{
|
||||
icon: Building2,
|
||||
titleKey: 'real_estate',
|
||||
descKey: 'real_estate_desc',
|
||||
color: '#d97706'
|
||||
},
|
||||
{
|
||||
icon: ShoppingCart,
|
||||
titleKey: 'retail',
|
||||
descKey: 'retail_desc',
|
||||
color: '#dc2626'
|
||||
},
|
||||
{
|
||||
icon: Truck,
|
||||
titleKey: 'wholesale',
|
||||
descKey: 'wholesale_desc',
|
||||
color: '#4f46e5'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<main class="main" class:mounted>
|
||||
<!-- Hero Section -->
|
||||
<section class="hero scroll-animate">
|
||||
<div class="container">
|
||||
<div class="hero-content">
|
||||
<h1 class="hero-title">
|
||||
{t('company_name', $language)}
|
||||
</h1>
|
||||
<p class="hero-description">
|
||||
{t('description', $language)}
|
||||
</p>
|
||||
<div class="nav-value">
|
||||
<strong>{t('nav_value', $language)}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Investment Areas -->
|
||||
<section class="investment-areas scroll-animate">
|
||||
<div class="container">
|
||||
<div class="areas-grid">
|
||||
{#each investmentAreas as area, index}
|
||||
<div class="area-card" style="--delay: {index * 0.1}s">
|
||||
<div class="area-icon" style="--icon-color: {area.color}">
|
||||
<svelte:component this={area.icon} size={32} />
|
||||
</div>
|
||||
<h3>{t(area.titleKey, $language)}</h3>
|
||||
<p>{t(area.descKey, $language)}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Mission & Vision -->
|
||||
<section class="mission-vision scroll-animate">
|
||||
<div class="container">
|
||||
<h2 class="section-title">{t('mission_title', $language)}</h2>
|
||||
|
||||
<div class="mv-grid">
|
||||
<div class="mv-card">
|
||||
<h3>{t('mission', $language)}</h3>
|
||||
<p>{t('mission_desc', $language)}</p>
|
||||
</div>
|
||||
|
||||
<div class="mv-card">
|
||||
<h3>{t('vision', $language)}</h3>
|
||||
<p>{t('vision_desc', $language)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Team -->
|
||||
<section class="team scroll-animate">
|
||||
<div class="container">
|
||||
<h2 class="section-title">{t('team_title', $language)}</h2>
|
||||
<p class="team-description">{t('team_desc', $language)}</p>
|
||||
|
||||
<div class="team-card">
|
||||
<div class="team-icon">
|
||||
<Users size={32} />
|
||||
</div>
|
||||
<div class="team-info">
|
||||
<h4>{t('fund_manager', $language)}</h4>
|
||||
<p>{t('manager_name', $language)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Partners -->
|
||||
<section class="partners scroll-animate">
|
||||
<div class="container">
|
||||
<h2 class="section-title">{t('partners_title', $language)}</h2>
|
||||
|
||||
<div class="partners-grid">
|
||||
<div class="partner-card">
|
||||
<h4>{t('auditing_firm', $language)}</h4>
|
||||
<p><strong>{t('auditing_name', $language)}</strong></p>
|
||||
<p>{t('auditing_address', $language)}</p>
|
||||
<p>{t('auditing_code', $language)}</p>
|
||||
</div>
|
||||
|
||||
<div class="partner-card">
|
||||
<h4>{t('depository', $language)}</h4>
|
||||
<p><strong>{t('depository_name', $language)}</strong></p>
|
||||
<p>{t('depository_address', $language)}</p>
|
||||
<p>{t('depository_code', $language)}</p>
|
||||
</div>
|
||||
|
||||
<div class="partner-card">
|
||||
<h4>{t('custodian', $language)}</h4>
|
||||
<p><strong>{t('custodian_name', $language)}</strong></p>
|
||||
<p>{t('custodian_address', $language)}</p>
|
||||
<p>{t('custodian_code', $language)}</p>
|
||||
</div>
|
||||
|
||||
<div class="partner-card">
|
||||
<h4>{t('valuer', $language)}</h4>
|
||||
<p><strong>{t('valuer_name', $language)}</strong></p>
|
||||
<p>{t('valuer_address', $language)}</p>
|
||||
<p>{t('valuer_code', $language)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.main {
|
||||
padding-top: 4rem;
|
||||
opacity: 0;
|
||||
transition: opacity 0.6s ease;
|
||||
}
|
||||
|
||||
.main.mounted {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
.scroll-animate {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
:global(.scroll-animate.animate-in) {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Hero Section */
|
||||
.hero {
|
||||
padding: 4rem 0;
|
||||
background: linear-gradient(135deg, var(--bg-color) 0%, var(--bg-secondary) 100%);
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: clamp(1.5rem, 4vw, 2.5rem);
|
||||
font-weight: 700;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 1.5rem;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero-description {
|
||||
font-size: 1.125rem;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
margin-bottom: 2rem;
|
||||
text-align: center;
|
||||
max-width: 800px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.nav-value {
|
||||
text-align: center;
|
||||
padding: 1.5rem;
|
||||
background: var(--bg-color);
|
||||
border: 2px solid var(--primary-color);
|
||||
border-radius: 1rem;
|
||||
font-size: 1.125rem;
|
||||
color: var(--primary-color);
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Investment Areas */
|
||||
.investment-areas {
|
||||
padding: 4rem 0;
|
||||
}
|
||||
|
||||
.areas-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 2rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.area-card {
|
||||
background: var(--bg-color);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 1rem;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease;
|
||||
animation: fadeInUp 0.8s ease forwards;
|
||||
animation-delay: var(--delay);
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
|
||||
.area-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.area-icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background: var(--icon-color);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1.5rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.area-card h3 {
|
||||
color: var(--text-color);
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.area-card p {
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Mission & Vision */
|
||||
.mission-vision {
|
||||
padding: 4rem 0;
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-color);
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.mv-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.mv-card {
|
||||
background: var(--bg-color);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 1rem;
|
||||
padding: 2rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.mv-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.mv-card h3 {
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.mv-card p {
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Team */
|
||||
.team {
|
||||
padding: 4rem 0;
|
||||
}
|
||||
|
||||
.team-description {
|
||||
text-align: center;
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.125rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.team-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
background: var(--bg-color);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 1rem;
|
||||
padding: 2rem;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.team-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.team-icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background: var(--primary-color);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.team-info h4 {
|
||||
color: var(--text-color);
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.team-info p {
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Partners */
|
||||
.partners {
|
||||
padding: 4rem 0;
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.partners-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.partner-card {
|
||||
background: var(--bg-color);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 1rem;
|
||||
padding: 1.5rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.partner-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.partner-card h4 {
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.partner-card p {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.partner-card p strong {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.hero {
|
||||
padding: 2rem 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.areas-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.mv-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.team-card {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.partners-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,10 @@
|
||||
<script>
|
||||
let count = $state(0)
|
||||
const increment = () => {
|
||||
count += 1
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick={increment}>
|
||||
count is {count}
|
||||
</button>
|
||||
@@ -0,0 +1,9 @@
|
||||
import { mount } from 'svelte'
|
||||
import './app.css'
|
||||
import App from './App.svelte'
|
||||
|
||||
const app = mount(App, {
|
||||
target: document.getElementById('app'),
|
||||
})
|
||||
|
||||
export default app
|
||||
@@ -0,0 +1,90 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
const browser = typeof window !== 'undefined';
|
||||
|
||||
// Theme store with three modes: system, light, dark
|
||||
function createThemeStore() {
|
||||
const { subscribe, set, update } = writable('system');
|
||||
|
||||
function applyTheme(mode) {
|
||||
if (!browser) return;
|
||||
|
||||
let actualTheme;
|
||||
if (mode === 'system') {
|
||||
actualTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
} else {
|
||||
actualTheme = mode;
|
||||
}
|
||||
document.documentElement.setAttribute('data-theme', actualTheme);
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
set: (theme) => {
|
||||
if (browser) {
|
||||
localStorage.setItem('theme', theme);
|
||||
applyTheme(theme);
|
||||
}
|
||||
set(theme);
|
||||
},
|
||||
init: () => {
|
||||
if (browser) {
|
||||
const saved = localStorage.getItem('theme') || 'system';
|
||||
applyTheme(saved);
|
||||
set(saved);
|
||||
|
||||
// Listen for system theme changes when in system mode
|
||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
mediaQuery.addEventListener('change', () => {
|
||||
const currentTheme = localStorage.getItem('theme') || 'system';
|
||||
if (currentTheme === 'system') {
|
||||
applyTheme('system');
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
cycle: () => {
|
||||
update(current => {
|
||||
const modes = ['system', 'light', 'dark'];
|
||||
const currentIndex = modes.indexOf(current);
|
||||
const nextMode = modes[(currentIndex + 1) % modes.length];
|
||||
|
||||
if (browser) {
|
||||
localStorage.setItem('theme', nextMode);
|
||||
applyTheme(nextMode);
|
||||
}
|
||||
|
||||
return nextMode;
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Language store
|
||||
function createLanguageStore() {
|
||||
const { subscribe, set, update } = writable('en');
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
set: (lang) => {
|
||||
if (browser) {
|
||||
localStorage.setItem('language', lang);
|
||||
document.documentElement.setAttribute('lang', lang);
|
||||
}
|
||||
set(lang);
|
||||
},
|
||||
init: () => {
|
||||
if (browser) {
|
||||
const saved = localStorage.getItem('language');
|
||||
const browserLang = navigator.language.split('-')[0];
|
||||
const supportedLangs = ['en', 'uk', 'ru'];
|
||||
const lang = saved || (supportedLangs.includes(browserLang) ? browserLang : 'en');
|
||||
document.documentElement.setAttribute('lang', lang);
|
||||
set(lang);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const theme = createThemeStore();
|
||||
export const language = createLanguageStore();
|
||||
@@ -0,0 +1,98 @@
|
||||
export const translations = {
|
||||
en: {
|
||||
company_name: 'JOINT STOCK COMPANY CLOSED NON-DIVERSIFIED VENTURE CORPORATE INVESTMENT FUND "VANCOUVER"',
|
||||
description: 'We are a Ukrainian investment fund established in 2018. Over more than six years of operation, we have focused our investment strategy on the following key areas:',
|
||||
energy: 'Energy',
|
||||
energy_desc: 'Investments in renewable energy, particularly in the Hirskyi Wind Park project, which provides clean energy and contributes to Ukraine\'s energy independence.',
|
||||
woodworking: 'Woodworking Industry',
|
||||
woodworking_desc: 'Partnership with SLAV FOREST — a modern woodworking enterprise that implements advanced wood processing technologies with a focus on export-oriented production.',
|
||||
real_estate: 'Office Real Estate',
|
||||
real_estate_desc: 'We invest in stable real estate assets located in the central business districts of Kyiv.',
|
||||
retail: 'Retail',
|
||||
retail_desc: 'Investment participation in the development of the Silpo chain — one of Ukraine\'s most successful retailers, focused on innovation in customer service.',
|
||||
wholesale: 'Wholesale Trade',
|
||||
wholesale_desc: 'Support of trading companies that provide logistics and distribution of goods both within Ukraine and internationally.',
|
||||
nav_value: 'As of the end of Q1 2025, the Fund\'s net asset value amounts to €16.8 million.',
|
||||
mission_title: 'Mission / Vision',
|
||||
mission: 'Mission',
|
||||
mission_desc: 'To generate stable long-term capital growth through strategic investments in promising economic sectors demonstrating sustainable development dynamics.',
|
||||
vision: 'Vision',
|
||||
vision_desc: 'We strive to become the investment partner of choice for Ukrainian and international investors seeking transparent, professional, and efficient capital placement mechanisms.',
|
||||
team_title: 'Team',
|
||||
team_desc: 'Key person responsible for the Fund\'s operations:',
|
||||
fund_manager: 'Fund Manager:',
|
||||
manager_name: 'Oleksandr Viktorovych Svynar — an experienced finance and investment manager responsible for the overall strategy and asset management.',
|
||||
partners_title: 'Fund Partners',
|
||||
auditing_firm: 'Auditing firm:',
|
||||
auditing_name: 'LLC "AUDITING CENTER "INFORM-PLUS"',
|
||||
auditing_address: 'Kyiv, 52 Bohdana Khmelnytskoho St., Office 504',
|
||||
auditing_code: 'EDRPOU Code: 31984899',
|
||||
depository: 'Depository:',
|
||||
depository_name: 'JSC "NATIONAL DEPOSITORY OF UKRAINE"',
|
||||
depository_address: 'Kyiv, 7-G Tropinina St.',
|
||||
depository_code: 'EDRPOU Code: 30370711',
|
||||
custodian: 'Custodian Institution:',
|
||||
custodian_name: 'LLC "INZHUR CAPITAL"',
|
||||
custodian_address: 'Kyiv, 48, 50A Zhylianska St., 7th Floor, Office 19',
|
||||
custodian_code: 'EDRPOU Code: 36301402',
|
||||
valuer: 'Property Valuer:',
|
||||
valuer_name: 'LLC "EXPERT-ALLIANCE"',
|
||||
valuer_address: 'Kyiv, 25 Lva Tolstoho St.',
|
||||
valuer_code: 'EDRPOU Code: 34289017',
|
||||
contact_title: 'Contact Information',
|
||||
manager_title: 'Manager: Oleksandr Viktorovych Svynar',
|
||||
office_address: 'Office Address:',
|
||||
address: '01033, Ukraine, Kyiv, 48, 50A Zhylianska St., Office 13',
|
||||
phone: 'Phone: +38 050 465 46 18'
|
||||
},
|
||||
uk: {
|
||||
company_name: 'АКЦІОНЕРНЕ ТОВАРИСТВО ЗАКРИТИЙ НЕДИВЕРСИФІКОВАНИЙ ВЕНЧУРНИЙ КОРПОРАТИВНИЙ ІНВЕСТИЦІЙНИЙ ФОНД "ВАНКУВЕР"',
|
||||
description: 'Ми — український інвестиційний фонд, заснований у 2018 році. За понад шість років роботи ми зосередили свою інвестиційну стратегію на наступних ключових напрямках:',
|
||||
energy: 'Енергетика',
|
||||
energy_desc: 'Інвестиції у відновлювану енергетику, зокрема у проект Гірського вітропарку, який забезпечує чисту енергію та сприяє енергетичній незалежності України.',
|
||||
woodworking: 'Деревообробна промисловість',
|
||||
woodworking_desc: 'Партнерство з SLAV FOREST — сучасним деревообробним підприємством, яке впроваджує передові технології обробки деревини з орієнтацією на експортне виробництво.',
|
||||
real_estate: 'Офісна нерухомість',
|
||||
real_estate_desc: 'Ми інвестуємо у стабільні активи нерухомості, розташовані в центральних діловий районах Києва.',
|
||||
retail: 'Роздрібна торгівля',
|
||||
retail_desc: 'Інвестиційна участь у розвитку мережі Silpo — одного з найуспішніших рітейлерів України, орієнтованого на інновації в обслуговуванні клієнтів.',
|
||||
wholesale: 'Оптова торгівля',
|
||||
wholesale_desc: 'Підтримка торговельних компаній, які забезпечують логістику та дистрибуцію товарів як в межах України, так і на міжнародному рівні.',
|
||||
nav_value: 'Станом на кінець І кварталу 2025 року чиста вартість активів Фонду становить 16,8 млн євро.',
|
||||
mission_title: 'Місія / Бачення',
|
||||
mission: 'Місія',
|
||||
mission_desc: 'Генерувати стабільне довгострокове зростання капіталу через стратегічні інвестиції у перспективні економічні сектори, що демонструють динаміку сталого розвитку.',
|
||||
vision: 'Бачення',
|
||||
vision_desc: 'Ми прагнемо стати інвестиційним партнером вибору для українських та міжнародних інвесторів, які шукають прозорі, професійні та ефективні механізми розміщення капіталу.',
|
||||
team_title: 'Команда',
|
||||
team_desc: 'Ключова особа, відповідальна за діяльність Фонду:',
|
||||
fund_manager: 'Управляючий фондом:',
|
||||
manager_name: 'Свинар Олександр Вікторович — досвідчений менеджер з фінансів та інвестицій, відповідальний за загальну стратегію та управління активами.',
|
||||
partners_title: 'Партнери фонду',
|
||||
auditing_firm: 'Аудиторська фірма:',
|
||||
auditing_name: 'ТОВ "АУДИТОРСЬКИЙ ЦЕНТР "ІНФОРМ-ПЛЮС"',
|
||||
auditing_address: 'м. Київ, вул. Богдана Хмельницького, 52, офіс 504',
|
||||
auditing_code: 'Код ЄДРПОУ: 31984899',
|
||||
depository: 'Депозитарій:',
|
||||
depository_name: 'АТ "НАЦІОНАЛЬНИЙ ДЕПОЗИТАРІЙ УКРАЇНИ"',
|
||||
depository_address: 'м. Київ, вул. Тропініна, 7-Г',
|
||||
depository_code: 'Код ЄДРПОУ: 30370711',
|
||||
custodian: 'Зберігач:',
|
||||
custodian_name: 'ТОВ "ІНЖУРА КАПІТАЛ"',
|
||||
custodian_address: 'м. Київ, вул. Жилянська, 48, 50А, 7-й поверх, офіс 19',
|
||||
custodian_code: 'Код ЄДРПОУ: 36301402',
|
||||
valuer: 'Оцінювач майна:',
|
||||
valuer_name: 'ТОВ "ЕКСПЕРТ-АЛЬЯНС"',
|
||||
valuer_address: 'м. Київ, вул. Льва Толстого, 25',
|
||||
valuer_code: 'Код ЄДРПОУ: 34289017',
|
||||
contact_title: 'Контактна інформація',
|
||||
manager_title: 'Менеджер: Свинар Олександр Вікторович',
|
||||
office_address: 'Адреса офісу:',
|
||||
address: '01033, Україна, м. Київ, вул. Жилянська, 48, 50А, офіс 13',
|
||||
phone: 'Телефон: +38 050 465 46 18'
|
||||
}
|
||||
};
|
||||
|
||||
export function t(key, lang) {
|
||||
return translations[lang]?.[key] || translations.en[key] || key;
|
||||
}
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
/// <reference types="svelte" />
|
||||
/// <reference types="vite/client" />
|
||||
Reference in New Issue
Block a user