mirror of
https://github.com/shadoll/vancouver.git
synced 2026-08-28 03:28:21 +00:00
feat: initial commit of Vancouver Investment Fund web application
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
README.md
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.DS_Store
|
||||||
|
*.tgz
|
||||||
|
*.tar.gz
|
||||||
|
.svelte-kit
|
||||||
|
coverage
|
||||||
|
.nyc_output
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# Copilot Instructions
|
||||||
|
|
||||||
|
<!-- Use this file to provide workspace-specific custom instructions to Copilot. For more details, visit https://code.visualstudio.com/docs/copilot/copilot-customization#_use-a-githubcopilotinstructionsmd-file -->
|
||||||
|
|
||||||
|
This is a Svelte project with the following specifications:
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
- Svelte with Vite (no webpack)
|
||||||
|
- Docker/Podman for local development
|
||||||
|
- GitHub Actions for CI/CD and GitHub Pages deployment
|
||||||
|
- SPA Router for routing
|
||||||
|
- Lucide Svelte for icons
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
- Component-based architecture with reusable components
|
||||||
|
- Global styles and component-specific styles
|
||||||
|
- Multilanguage support with browser detection
|
||||||
|
- Theme switching (light/dark mode) with system detection
|
||||||
|
- Responsive design for mobile and desktop
|
||||||
|
- Local storage for user preferences
|
||||||
|
- Smooth animations and scroll effects
|
||||||
|
|
||||||
|
## Key Components
|
||||||
|
- Header (with language switcher and theme switcher)
|
||||||
|
- Footer
|
||||||
|
- LanguageSwitcher
|
||||||
|
- ThemeSwitcher
|
||||||
|
- Main content components
|
||||||
|
|
||||||
|
## Styling Guidelines
|
||||||
|
- Use CSS custom properties for theming
|
||||||
|
- Mobile-first responsive design
|
||||||
|
- Smooth transitions and animations
|
||||||
|
- Modern, clean design aesthetic
|
||||||
|
|
||||||
|
## State Management
|
||||||
|
- Use Svelte stores for global state
|
||||||
|
- Store user preferences (theme, language) in localStorage
|
||||||
|
- Detect browser language and system theme as defaults
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
name: Build and Deploy to GitHub Pages
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: "pages"
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: npm run build
|
||||||
|
env:
|
||||||
|
NODE_ENV: production
|
||||||
|
|
||||||
|
- name: Setup Pages
|
||||||
|
uses: actions/configure-pages@v4
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-pages-artifact@v3
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
with:
|
||||||
|
path: './dist'
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
id: deployment
|
||||||
|
uses: actions/deploy-pages@v4
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["svelte.svelte-vscode"]
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
FROM node:20-alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy package files first for better Docker layer caching
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 5173
|
||||||
|
|
||||||
|
# Start development server with hot reload
|
||||||
|
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0", "--port", "5173"]
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
.PHONY: help dev build clean install test stop detect-container-runtime
|
||||||
|
|
||||||
|
# Container runtime detection
|
||||||
|
CONTAINER_RUNTIME := $(shell command -v podman >/dev/null 2>&1 && echo "podman" || echo "docker")
|
||||||
|
COMPOSE_CMD := $(CONTAINER_RUNTIME)-compose
|
||||||
|
|
||||||
|
# Default target
|
||||||
|
help:
|
||||||
|
@echo "Available commands:"
|
||||||
|
@echo " dev - Start development server in container"
|
||||||
|
@echo " build - Build production version (static files for GitHub Pages)"
|
||||||
|
@echo " install - Install dependencies in container"
|
||||||
|
@echo " clean - Clean build files and node_modules"
|
||||||
|
@echo " test - Run tests in container"
|
||||||
|
@echo " stop - Stop all containers"
|
||||||
|
@echo ""
|
||||||
|
@echo "Detected container runtime: $(CONTAINER_RUNTIME)"
|
||||||
|
|
||||||
|
# Development (container only)
|
||||||
|
dev:
|
||||||
|
$(COMPOSE_CMD) up app
|
||||||
|
|
||||||
|
# Build for GitHub Pages (static files)
|
||||||
|
build:
|
||||||
|
$(COMPOSE_CMD) run --rm app npm run build
|
||||||
|
@echo "Static files built in ./dist directory"
|
||||||
|
@echo "Ready for GitHub Pages deployment"
|
||||||
|
|
||||||
|
# Install dependencies in container
|
||||||
|
install:
|
||||||
|
$(COMPOSE_CMD) run --rm app npm install
|
||||||
|
|
||||||
|
# Clean
|
||||||
|
clean:
|
||||||
|
rm -rf node_modules dist .svelte-kit
|
||||||
|
$(COMPOSE_CMD) down --volumes --remove-orphans
|
||||||
|
|
||||||
|
# Test in container
|
||||||
|
test:
|
||||||
|
$(COMPOSE_CMD) run --rm app npm test
|
||||||
|
|
||||||
|
# Stop all containers
|
||||||
|
stop:
|
||||||
|
$(COMPOSE_CMD) down
|
||||||
|
|
||||||
|
# Show detected container runtime
|
||||||
|
detect-container-runtime:
|
||||||
|
@echo "Detected container runtime: $(CONTAINER_RUNTIME)"
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
# Vancouver Investment Fund - Official Website
|
||||||
|
|
||||||
|
A modern, responsive website for the Vancouver Investment Fund built with Svelte and Vite.
|
||||||
|
|
||||||
|
## 🚀 Features
|
||||||
|
|
||||||
|
- **Modern UI/UX**: Clean, professional design with smooth animations
|
||||||
|
- **Multilingual Support**: English and Ukrainian languages with automatic browser detection
|
||||||
|
- **Theme Switching**: Light/dark mode with system preference detection
|
||||||
|
- **Responsive Design**: Mobile-first approach, works on all devices
|
||||||
|
- **Fast Performance**: Built with Vite for optimal loading speeds
|
||||||
|
- **Docker Support**: Easy development and deployment with Docker/Podman
|
||||||
|
- **CI/CD Pipeline**: Automated deployment to GitHub Pages
|
||||||
|
- **Component Architecture**: Reusable components for scalability
|
||||||
|
|
||||||
|
## 🛠 Tech Stack
|
||||||
|
|
||||||
|
- **Frontend**: Svelte 5 + Vite
|
||||||
|
- **Styling**: CSS Custom Properties for theming
|
||||||
|
- **Icons**: Lucide Svelte
|
||||||
|
- **Routing**: Svelte SPA Router (ready for multi-page expansion)
|
||||||
|
- **Deployment**: GitHub Pages via GitHub Actions
|
||||||
|
- **Development**: Docker/Podman support
|
||||||
|
|
||||||
|
## 🏃♂️ Quick Start
|
||||||
|
|
||||||
|
### Using Make (Recommended)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install dependencies in container
|
||||||
|
make install
|
||||||
|
|
||||||
|
# Start development server with hot reload (automatic rebuild on file changes)
|
||||||
|
make dev
|
||||||
|
|
||||||
|
# Build for production (static files for GitHub Pages)
|
||||||
|
make build
|
||||||
|
|
||||||
|
# Clean build files and containers
|
||||||
|
make clean
|
||||||
|
|
||||||
|
# Stop all containers
|
||||||
|
make stop
|
||||||
|
```
|
||||||
|
|
||||||
|
The `make dev` command will:
|
||||||
|
- Automatically detect Docker or Podman
|
||||||
|
- Start the development server in a container
|
||||||
|
- Watch for file changes and rebuild automatically
|
||||||
|
- Enable hot module replacement (HMR) for instant updates
|
||||||
|
|
||||||
|
### Manual Commands (in container)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install dependencies
|
||||||
|
make install
|
||||||
|
|
||||||
|
# Start development server
|
||||||
|
make dev
|
||||||
|
|
||||||
|
# Build for production
|
||||||
|
make build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Development with Docker
|
||||||
|
docker-compose up app
|
||||||
|
|
||||||
|
# Production with Docker
|
||||||
|
docker-compose --profile production up app-prod
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📁 Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── components/ # Reusable UI components
|
||||||
|
│ ├── Header.svelte # Navigation with theme/language switchers
|
||||||
|
│ ├── Footer.svelte # Footer component
|
||||||
|
│ └── Home.svelte # Main page content
|
||||||
|
├── stores.js # Global state management
|
||||||
|
├── translations.js # Multi-language support
|
||||||
|
├── app.css # Global styles and CSS variables
|
||||||
|
├── App.svelte # Root component
|
||||||
|
└── main.js # Application entry point
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🌍 Internationalization
|
||||||
|
|
||||||
|
The website supports multiple languages with automatic browser detection:
|
||||||
|
- English (en) - Default
|
||||||
|
- Ukrainian (uk)
|
||||||
|
|
||||||
|
Language preferences are stored in localStorage and persist across sessions.
|
||||||
|
|
||||||
|
## 🎨 Theming
|
||||||
|
|
||||||
|
The application supports light and dark themes with:
|
||||||
|
- Automatic system preference detection
|
||||||
|
- Manual theme switching
|
||||||
|
- Persistent user preferences
|
||||||
|
- CSS custom properties for consistent theming
|
||||||
|
|
||||||
|
## 🚀 Deployment
|
||||||
|
|
||||||
|
### GitHub Pages (Automatic)
|
||||||
|
|
||||||
|
Push to the `main` branch to trigger automatic deployment to GitHub Pages via GitHub Actions.
|
||||||
|
|
||||||
|
### Manual Deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build the project
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Deploy the dist/ folder to your hosting provider
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🐳 Container Development
|
||||||
|
|
||||||
|
The project automatically detects and uses Docker or Podman:
|
||||||
|
|
||||||
|
### Development with Hot Reload
|
||||||
|
```bash
|
||||||
|
# Automatically detects container runtime (Docker/Podman)
|
||||||
|
make dev
|
||||||
|
|
||||||
|
# Or manually specify:
|
||||||
|
docker-compose up app
|
||||||
|
# OR
|
||||||
|
podman-compose up app
|
||||||
|
```
|
||||||
|
|
||||||
|
### Features:
|
||||||
|
- **Automatic file watching**: Changes to source files trigger instant rebuilds
|
||||||
|
- **Hot Module Replacement (HMR)**: See changes without page refresh
|
||||||
|
- **Volume mounting**: Source code changes are reflected immediately
|
||||||
|
- **Container runtime detection**: Works with both Docker and Podman
|
||||||
|
|
||||||
|
### Container Configuration:
|
||||||
|
- **Port**: 5173 (accessible at http://localhost:5173)
|
||||||
|
- **Polling**: Enabled for file watching in containers
|
||||||
|
- **HMR**: Configured for container networking
|
||||||
|
- **Volume mounts**: Source code and node_modules are properly mounted
|
||||||
|
|
||||||
|
## 📝 Available Scripts
|
||||||
|
|
||||||
|
- `make dev` - Start development server
|
||||||
|
- `make build` - Build for production
|
||||||
|
- `make prod` - Start production server
|
||||||
|
- `make dev-docker` - Start development with Docker
|
||||||
|
- `make prod-docker` - Start production with Docker
|
||||||
|
- `make clean` - Clean build files and dependencies
|
||||||
|
- `make help` - Show all available commands
|
||||||
|
|
||||||
|
## 🤝 Contributing
|
||||||
|
|
||||||
|
1. Fork the repository
|
||||||
|
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
||||||
|
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
||||||
|
4. Push to the branch (`git push origin feature/amazing-feature`)
|
||||||
|
5. Open a Pull Request
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
This project is proprietary and confidential.
|
||||||
|
|
||||||
|
## 📞 Contact
|
||||||
|
|
||||||
|
Vancouver Investment Fund
|
||||||
|
- Address: 01033, Ukraine, Kyiv, 48, 50A Zhylianska St., Office 13
|
||||||
|
- Phone: +38 050 465 46 18
|
||||||
|
- Manager: Oleksandr Viktorovych Svynar
|
||||||
|
|
||||||
|
**Why enable `checkJs` in the JS template?**
|
||||||
|
|
||||||
|
It is likely that most cases of changing variable types in runtime are likely to be accidental, rather than deliberate. This provides advanced typechecking out of the box. Should you like to take advantage of the dynamically-typed nature of JavaScript, it is trivial to change the configuration.
|
||||||
|
|
||||||
|
**Why is HMR not preserving my local component state?**
|
||||||
|
|
||||||
|
HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/sveltejs/svelte-hmr/tree/master/packages/svelte-hmr#preservation-of-local-state).
|
||||||
|
|
||||||
|
If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// store.js
|
||||||
|
// An extremely simple external store
|
||||||
|
import { writable } from 'svelte/store'
|
||||||
|
export default writable(0)
|
||||||
|
```
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "5173:5173"
|
||||||
|
volumes:
|
||||||
|
- .:/app
|
||||||
|
- /app/node_modules
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=development
|
||||||
|
- VITE_HMR_PORT=5173
|
||||||
|
- CHOKIDAR_USEPOLLING=true
|
||||||
|
- CHOKIDAR_INTERVAL=1000
|
||||||
|
command: npm run dev -- --host 0.0.0.0 --port 5173
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vite + Svelte</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
/**
|
||||||
|
* svelte-preprocess cannot figure out whether you have
|
||||||
|
* a value or a type, so tell TypeScript to enforce using
|
||||||
|
* `import type` instead of `import` for Types.
|
||||||
|
*/
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
/**
|
||||||
|
* To have warnings / errors of the Svelte compiler at the
|
||||||
|
* correct position, enable source maps by default.
|
||||||
|
*/
|
||||||
|
"sourceMap": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
/**
|
||||||
|
* Typecheck JS in `.svelte` and `.js` files by default.
|
||||||
|
* Disable this if you'd like to use dynamic types.
|
||||||
|
*/
|
||||||
|
"checkJs": true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Use global.d.ts instead of compilerOptions.types
|
||||||
|
* to avoid limiting type declarations.
|
||||||
|
*/
|
||||||
|
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
|
||||||
|
}
|
||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
sendfile on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_min_length 1024;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
gzip_types
|
||||||
|
text/plain
|
||||||
|
text/css
|
||||||
|
text/xml
|
||||||
|
text/javascript
|
||||||
|
application/json
|
||||||
|
application/javascript
|
||||||
|
application/xml+rss
|
||||||
|
application/atom+xml
|
||||||
|
image/svg+xml;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Handle client routing for SPA
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cache static assets
|
||||||
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||||
|
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+1392
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"name": "vancouver",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"description": "Vancouver Investment Fund - Official Website",
|
||||||
|
"keywords": [
|
||||||
|
"svelte",
|
||||||
|
"investment",
|
||||||
|
"fund",
|
||||||
|
"ukraine",
|
||||||
|
"vancouver"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"lint": "svelte check --tsconfig ./jsconfig.json",
|
||||||
|
"format": "prettier --write ."
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
||||||
|
"svelte": "^5.28.1",
|
||||||
|
"terser": "^5.43.1",
|
||||||
|
"vite": "^6.3.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"lucide-svelte": "^0.518.0",
|
||||||
|
"svelte-spa-router": "^4.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -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" />
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// Consult https://svelte.dev/docs#compile-time-svelte-preprocess
|
||||||
|
// for more information about preprocessors
|
||||||
|
preprocess: vitePreprocess(),
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import { svelte } from '@sveltejs/vite-plugin-svelte'
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [svelte()],
|
||||||
|
base: process.env.NODE_ENV === 'production' ? '/vancouver/' : '/',
|
||||||
|
build: {
|
||||||
|
outDir: 'dist',
|
||||||
|
assetsDir: 'assets',
|
||||||
|
sourcemap: false,
|
||||||
|
minify: 'terser',
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
host: true,
|
||||||
|
port: 5173,
|
||||||
|
watch: {
|
||||||
|
usePolling: true,
|
||||||
|
interval: 1000,
|
||||||
|
},
|
||||||
|
hmr: {
|
||||||
|
port: 5173,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user